Java创建线程的三种方式

本文将详细讲解Java中创建线程的三种主要方式:Thread、Runnable和Callable,帮助你深入理解它们的原理、使用场景和优缺点。

Thread方式创建线程

基本用法

Thread是Java中最直接创建线程的方式,通过继承Thread类并重写run()方法来定义线程任务。

方式1:定义Thread子类
// 定义线程类
public class MyThread extends Thread {
    @Override
    public void run() {
        // 线程要执行的任务
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + 
                             " 执行:" + i);
        }
    }
}

// 使用线程
public class ThreadDemo {
    public static void main(String[] args) {
        // 创建线程对象
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
        
        // 启动线程(注意:必须调用start(),不是run())
        t1.start();
        t2.start();
        
        // 主线程继续执行
        for (int i = 0; i < 100; i++) {
            System.out.println("main线程执行:" + i);
        }
    }
}
方式2:匿名内部类
public class ThreadDemo {
    public static void main(String[] args) {
        // 使用匿名内部类创建线程
        Thread t1 = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < 50; i++) {
                    System.out.println(getName() + " 执行:" + i);
                }
            }
        };
        t1.start();
        
        // 主线程任务
        for (int i = 0; i < 50; i++) {
            System.out.println("main线程执行:" + i);
        }
    }
}

Thread构造器

Thread类提供了多个构造器:

// 无参构造
public Thread()

// 指定线程名称
public Thread(String name)

// 指定Runnable任务
public Thread(Runnable target)

// 指定Runnable任务和线程名称
public Thread(Runnable target, String name)

// 指定线程组和Runnable任务
public Thread(ThreadGroup group, Runnable target)

// 更多构造器...

使用示例

// 创建带名称的线程
Thread t1 = new MyThread();
t1.setName("工作线程-1"); // 设置名称

// 或直接通过构造器设置
Thread t2 = new Thread("工作线程-2") {
    @Override
    public void run() {
        System.out.println(getName() + " 正在运行");
    }
};

优缺点分析

✅ 优点
  1. 编码简单:直接继承Thread类,重写run方法即可
  2. 直接操作:可以直接在类中操作Thread的方法
public class MyThread extends Thread {
    @Override
    public void run() {
        // 可以直接使用getName()、getId()等方法
        System.out.println("线程名: " + getName());
        System.out.println("线程ID: " + getId());
    }
}
❌ 缺点
  1. 单继承限制:Java只支持单继承,继承Thread后无法继承其他类
  2. 耦合度高:线程任务和线程本身耦合在一起,不利于复用
// ❌ 错误示例:无法再继承其他类
public class MyThread extends Thread, SomeOtherClass { // 编译错误!
    // ...
}

// ✅ 正确做法:使用Runnable接口
public class MyTask extends SomeOtherClass implements Runnable {
    @Override
    public void run() {
        // ...
    }
}

Runnable方式创建线程

基本原理

Runnable是一个函数式接口,只包含一个抽象方法run():

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

Thread类本身也实现了Runnable接口

public class Thread implements Runnable {
    private Runnable target; // 持有Runnable对象
    
    public Thread(Runnable target) {
        this.target = target;
    }
    
    @Override
    public void run() {
        // 如果有target,执行target的run方法
        if (target != null) {
            target.run();
        }
    }
}

基本用法

方式1:实现Runnable接口
// 定义任务类
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + 
                             " -> " + i);
        }
    }
}

// 使用任务
public class RunnableDemo {
    public static void main(String[] args) {
        // 创建任务对象
        Runnable task = new MyRunnable();
        
        // 把任务对象包装成线程对象
        Thread t1 = new Thread(task, "1号线程");
        Thread t2 = new Thread(task, "2号线程");
        
        // 启动线程
        t1.start();
        t2.start();
    }
}
方式2:匿名内部类
public class RunnableDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + 
                                     " -> " + i);
                }
            }
        }, "工作线程");
        
        t1.start();
    }
}
方式3:Lambda表达式(推荐)

因为Runnable是函数式接口,可以使用Lambda表达式简化:

public class RunnableDemo {
    public static void main(String[] args) {
        // 使用Lambda表达式
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + 
                                 " -> " + i);
            }
        }, "工作线程");
        
        t1.start();
    }
}

优缺点分析

✅ 优点
  1. 避免单继承限制:实现接口的方式,还可以继承其他类
public class MyTask extends BaseClass implements Runnable {
    @Override
    public void run() {
        // 可以同时继承和实现接口
    }
}
  1. 资源共享:同一个Runnable对象可以被多个线程共享
public class TicketRunnable implements Runnable {
    private int ticketCount = 100; // 共享的票数
    
    @Override
    public void run() {
        while (ticketCount > 0) {
            System.out.println(Thread.currentThread().getName() + 
                             " 卖出第" + ticketCount-- + "张票");
        }
    }
}

// 多个线程共享同一个任务对象
Runnable task = new TicketRunnable();
new Thread(task, "窗口1").start();
new Thread(task, "窗口2").start();
new Thread(task, "窗口3").start();
  1. 解耦设计:线程任务代码与线程对象分离,代码更灵活
// 任务可以独立测试和复用
Runnable task = new DataProcessTask();

// 可以用不同方式执行同一个任务
new Thread(task).start();           // 方式1:新线程
task.run();                         // 方式2:当前线程
executorService.execute(task);      // 方式3:线程池
  1. 适合线程池:线程池接受Runnable和Callable任务
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new MyRunnable()); // 提交Runnable任务
❌ 缺点
  1. 代码稍复杂:相比Thread方式多了一层包装
  2. 无返回值:run()方法没有返回值

Callable方式创建线程

基本原理

Callable是一个泛型接口,可以返回结果并且可以抛出异常:

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

关键组件

  • Callable<V>:定义有返回值的任务
  • FutureTask<V>:包装Callable,实现Runnable接口
  • Future<V>:表示异步计算的结果

关系图

Callable<V>
    ↓ (包装)
FutureTask<V> (implements RunnableFuture<V>)
    ↓ (implements)
Runnable + Future
    ↓ (传入)
Thread

基本用法

完整示例
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

// 1. 定义任务类,实现Callable接口
public class MyCallable implements Callable<String> {
    private int n;
    
    public MyCallable(int n) {
        this.n = n;
    }
    
    @Override
    public String call() throws Exception {
        // 模拟耗时计算
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
            Thread.sleep(10); // 模拟耗时
        }
        return Thread.currentThread().getName() + 
               " 计算1到" + n + "的和为:" + sum;
    }
}

// 2. 使用Callable
public class CallableDemo {
    public static void main(String[] args) {
        try {
            // 创建Callable任务对象
            Callable<String> callable = new MyCallable(100);
            
            // 把Callable包装成FutureTask(未来任务对象)
            FutureTask<String> futureTask = new FutureTask<>(callable);
            
            // 把FutureTask包装成Thread
            Thread t1 = new Thread(futureTask, "计算线程");
            
            // 启动线程
            t1.start();
            
            // 主线程继续执行其他任务
            System.out.println("主线程继续执行其他任务...");
            
            // 获取线程执行结果(会阻塞等待)
            String result = futureTask.get();
            System.out.println("获取到结果:" + result);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

FutureTask详解

FutureTask是一个可以取消的异步计算任务,它实现了RunnableFuture接口:

public class FutureTask<V> implements RunnableFuture<V> {
    // 内部状态
    private volatile int state;
    private static final int NEW          = 0; // 新建
    private static final int COMPLETING   = 1; // 完成中
    private static final int NORMAL       = 2; // 正常完成
    private static final int EXCEPTIONAL  = 3; // 异常
    private static final int CANCELLED    = 4; // 已取消
    
    // 保存Callable任务
    private Callable<V> callable;
    
    // 保存执行结果
    private Object outcome;
    
    // 构造器
    public FutureTask(Callable<V> callable) {
        this.callable = callable;
        this.state = NEW;
    }
    
    // 执行任务
    public void run() {
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                try {
                    result = c.call(); // 调用Callable的call方法
                    set(result); // 设置结果
                } catch (Throwable ex) {
                    setException(ex); // 设置异常
                }
            }
        } finally {
            // ...
        }
    }
    
    // 获取结果
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L); // 等待任务完成
        return report(s); // 返回结果
    }
}

常用方法

1. get() - 获取结果
// 阻塞等待,直到任务完成
V get() throws InterruptedException, ExecutionException

// 限时等待
V get(long timeout, TimeUnit unit) throws InterruptedException, 
                                          ExecutionException, 
                                          TimeoutException

使用示例

FutureTask<Integer> task = new FutureTask<>(() -> {
    Thread.sleep(2000);
    return 42;
});

new Thread(task).start();

// 方式1:无限等待
Integer result = task.get();

// 方式2:限时等待(推荐)
try {
    Integer result = task.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    System.out.println("任务超时!");
    task.cancel(true); // 取消任务
}
2. cancel() - 取消任务
boolean cancel(boolean mayInterruptIfRunning)
FutureTask<String> task = new FutureTask<>(() -> {
    for (int i = 0; i < 10; i++) {
        if (Thread.currentThread().isInterrupted()) {
            return "任务被中断";
        }
        Thread.sleep(1000);
    }
    return "任务完成";
});

new Thread(task).start();
Thread.sleep(2000);

// 取消任务(尝试中断正在执行的任务)
boolean cancelled = task.cancel(true);
System.out.println("取消成功:" + cancelled);
3. 状态查询方法
boolean isDone()       // 任务是否完成
boolean isCancelled()  // 任务是否被取消
System.out.println("任务是否完成:" + task.isDone());
System.out.println("任务是否取消:" + task.isCancelled());

优缺点分析

✅ 优点
  1. 可以获取返回值:这是Callable最大的优势
Callable<Integer> task = () -> {
    // 复杂计算...
    return 42;
};
FutureTask<Integer> future = new FutureTask<>(task);
new Thread(future).start();
Integer result = future.get(); // 获取返回值
  1. 可以抛出异常:call()方法可以抛出受检异常
Callable<String> task = () -> {
    if (someCondition) {
        throw new Exception("出错了!");
    }
    return "成功";
};

try {
    String result = futureTask.get();
} catch (ExecutionException e) {
    Throwable cause = e.getCause(); // 获取原始异常
    System.out.println("任务执行出错:" + cause.getMessage());
}
  1. 适合需要结果的场景:如异步计算、数据查询等
// 异步查询用户信息
Callable<User> queryUser = () -> userService.getUserById(123);
FutureTask<User> future = new FutureTask<>(queryUser);
executor.execute(future);

// 主线程继续其他工作...
doOtherWork();

// 需要时获取结果
User user = future.get();
❌ 缺点
  1. 编码复杂:需要FutureTask包装,代码较多
  2. 阻塞获取结果:get()方法会阻塞当前线程
// ❌ 可能导致主线程长时间阻塞
String result = futureTask.get(); // 如果任务很慢,这里会一直等待

// ✅ 建议使用超时机制
try {
    String result = futureTask.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    System.out.println("任务超时");
    futureTask.cancel(true);
}

三种方式对比

对比表格

特性 Thread Runnable Callable
实现方式 继承Thread类 实现Runnable接口 实现Callable接口
方法签名 void run() void run() V call() throws Exception
返回值 ❌ 无 ❌ 无 ✅ 有
异常处理 无法抛出受检异常 无法抛出受检异常 可以抛出受检异常
单继承限制 ❌ 有限制 ✅ 无限制 ✅ 无限制
资源共享 不便共享 ✅ 便于共享 ✅ 便于共享
线程池支持 不推荐 ✅ 支持 ✅ 支持
代码复杂度 ⭐ 简单 ⭐⭐ 一般 ⭐⭐⭐ 较复杂
使用场景 简单任务 一般任务 需要返回值的任务

使用建议

🎯 选择Thread的场景
// ✅ 适用于:简单的、一次性的线程任务
new Thread() {
    @Override
    public void run() {
        System.out.println("简单任务");
    }
}.start();
🎯 选择Runnable的场景
// ✅ 适用于:
// 1. 需要继承其他类
// 2. 多个线程共享资源
// 3. 与线程池配合使用

// 场景1:需要继承
public class MyTask extends BaseClass implements Runnable {
    @Override
    public void run() {
        // ...
    }
}

// 场景2:资源共享
Runnable task = new TicketSeller();
new Thread(task, "窗口1").start();
new Thread(task, "窗口2").start();

// 场景3:线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
pool.execute(() -> System.out.println("任务执行"));
🎯 选择Callable的场景
// ✅ 适用于:
// 1. 需要获取执行结果
// 2. 需要处理异常
// 3. 异步计算场景

// 场景1:需要返回值
Callable<Integer> sumTask = () -> {
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
        sum += i;
    }
    return sum;
};

// 场景2:异步计算
ExecutorService pool = Executors.newFixedThreadPool(5);
Future<String> future = pool.submit(() -> {
    // 复杂计算...
    return "计算结果";
});
String result = future.get();

深入理解start和run

start()方法

作用:启动新线程,由JVM调用该线程的run()方法。

public synchronized void start() {
    // 1. 检查线程状态
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    
    // 2. 加入线程组
    group.add(this);
    
    boolean started = false;
    try {
        // 3. 调用native方法启动线程
        start0(); // 这是一个native方法
        started = true;
    } finally {
        // ...
    }
}

// native方法,由JVM实现
private native void start0();

底层流程

调用start()
    ↓
JVM调用start0()(native方法)
    ↓
操作系统创建新线程
    ↓
新线程进入就绪状态
    ↓
获得CPU时间片
    ↓
JVM回调run()方法
    ↓
线程执行

run()方法

本质:一个普通的实例方法。

@Override
public void run() {
    if (target != null) {
        target.run(); // 执行Runnable的run方法
    }
}

start() vs run() 对比

❌ 错误:直接调用run()
Thread t1 = new Thread(() -> {
    System.out.println("当前线程:" + Thread.currentThread().getName());
});

t1.run(); // ❌ 错误:在main线程中同步执行
// 输出:当前线程:main(不是t1!)

后果

  • ❌ 不会创建新线程
  • ❌ 在当前线程(如main)中同步执行
  • ❌ 失去多线程的意义
✅ 正确:调用start()
Thread t1 = new Thread(() -> {
    System.out.println("当前线程:" + Thread.currentThread().getName());
});

t1.start(); // ✅ 正确:启动新线程
// 输出:当前线程:Thread-0(新线程!)

效果

  • ✅ 创建新线程
  • ✅ 新线程与main线程并发执行
  • ✅ 体现多线程的优势

形象比喻

start() = 聘请一个新员工(新线程)
run()   = 自己干活(当前线程)

// 调用start()
老板(main线程): "小李(新线程),你去做这个任务"
小李: "好的!" (并行工作)
老板: 继续做自己的事情

// 直接调用run()
老板(main线程): "算了,我自己做吧"
老板: 亲自做这个任务(没有小李什么事)

实验验证

public class StartVsRun {
    public static void main(String[] args) {
        System.out.println("=== 测试run() ===");
        testRun();
        
        System.out.println("\n=== 测试start() ===");
        testStart();
    }
    
    // 直接调用run()
    private static void testRun() {
        Thread t = new Thread(() -> {
            System.out.println("  任务线程: " + 
                             Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "WorkThread");
        
        long start = System.currentTimeMillis();
        t.run(); // 直接调用run
        System.out.println("  主线程: " + Thread.currentThread().getName());
        System.out.println("  耗时: " + (System.currentTimeMillis() - start) + "ms");
    }
    
    // 调用start()
    private static void testStart() {
        Thread t = new Thread(() -> {
            System.out.println("  任务线程: " + 
                             Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "WorkThread");
        
        long start = System.currentTimeMillis();
        t.start(); // 调用start
        System.out.println("  主线程: " + Thread.currentThread().getName());
        System.out.println("  耗时: " + (System.currentTimeMillis() - start) + "ms");
    }
}

运行结果

=== 测试run() ===
  任务线程: main           ← run在main线程执行
  主线程: main
  耗时: 1000ms            ← 阻塞了1秒

=== 测试start() ===
  主线程: main
  耗时: 0ms               ← 立即返回,不阻塞
  任务线程: WorkThread    ← 在新线程执行

最佳实践

1. 优先使用Runnable或Callable

// ✅ 推荐
class MyTask implements Runnable {
    @Override
    public void run() {
        // ...
    }
}

// ❌ 不推荐(除非确实需要继承Thread)
class MyThread extends Thread {
    @Override
    public void run() {
        // ...
    }
}

原因

  • 避免单继承限制
  • 代码更灵活,易于测试
  • 便于与线程池配合

2. 使用Lambda表达式简化代码

// ❌ 繁琐写法
Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
});

// ✅ 简洁写法
Thread t2 = new Thread(() -> System.out.println("Hello"));

3. 给线程命名

// ✅ 良好实践:给线程起有意义的名字
Thread t = new Thread(() -> {
    // ...
}, "数据处理线程-1");

// 或者
Thread t = new Thread(() -> {
    // ...
});
t.setName("数据处理线程-1");

好处

  • 便于调试和日志分析
  • 出现问题时容易定位

4. 处理异常

// ✅ Runnable:内部处理异常
Thread t = new Thread(() -> {
    try {
        // 可能抛出异常的代码
        riskyOperation();
    } catch (Exception e) {
        // 必须在内部捕获
        logger.error("任务执行失败", e);
    }
});

// ✅ Callable:可以向外抛出异常
Callable<String> task = () -> {
    // 可以直接抛出异常
    if (error) {
        throw new Exception("出错了");
    }
    return "成功";
};

5. 使用线程池而不是直接创建线程

// ❌ 不推荐:频繁创建销毁线程
for (int i = 0; i < 1000; i++) {
    new Thread(() -> doWork()).start();
}

// ✅ 推荐:使用线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
    pool.execute(() -> doWork());
}
pool.shutdown();

实战案例

案例1:多线程下载器

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class MultiThreadDownloader {
    
    // 使用Callable实现带返回值的下载任务
    static class DownloadTask implements Callable<DownloadResult> {
        private String url;
        private String filename;
        
        public DownloadTask(String url, String filename) {
            this.url = url;
            this.filename = filename;
        }
        
        @Override
        public DownloadResult call() throws Exception {
            long startTime = System.currentTimeMillis();
            long fileSize = 0;
            
            try {
                URL downloadUrl = new URL(url);
                HttpURLConnection conn = (HttpURLConnection) downloadUrl.openConnection();
                
                // 获取文件大小
                fileSize = conn.getContentLengthLong();
                
                // 下载文件
                try (InputStream in = conn.getInputStream();
                     FileOutputStream out = new FileOutputStream(filename)) {
                    
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    long totalRead = 0;
                    
                    while ((bytesRead = in.read(buffer)) != -1) {
                        out.write(buffer, 0, bytesRead);
                        totalRead += bytesRead;
                        
                        // 打印进度
                        int progress = (int) (totalRead * 100 / fileSize);
                        System.out.printf("[%s] 下载进度: %d%%\r", 
                                        filename, progress);
                    }
                }
                
                long endTime = System.currentTimeMillis();
                return new DownloadResult(filename, fileSize, endTime - startTime, true);
                
            } catch (Exception e) {
                return new DownloadResult(filename, fileSize, 0, false);
            }
        }
    }
    
    // 下载结果
    static class DownloadResult {
        String filename;
        long fileSize;
        long timeMillis;
        boolean success;
        
        public DownloadResult(String filename, long fileSize, long timeMillis, boolean success) {
            this.filename = filename;
            this.fileSize = fileSize;
            this.timeMillis = timeMillis;
            this.success = success;
        }
        
        @Override
        public String toString() {
            if (success) {
                double speed = fileSize / 1024.0 / 1024.0 / (timeMillis / 1000.0);
                return String.format("%s 下载成功!大小: %.2fMB, 耗时: %.2fs, 速度: %.2fMB/s",
                        filename, fileSize / 1024.0 / 1024.0, timeMillis / 1000.0, speed);
            } else {
                return filename + " 下载失败!";
            }
        }
    }
    
    public static void main(String[] args) throws Exception {
        // 模拟下载任务
        String[] urls = {
            "http://example.com/file1.zip",
            "http://example.com/file2.zip",
            "http://example.com/file3.zip"
        };
        
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        
        // 提交所有下载任务
        List<Future<DownloadResult>> futures = new ArrayList<>();
        for (int i = 0; i < urls.length; i++) {
            DownloadTask task = new DownloadTask(urls[i], "file" + (i + 1) + ".zip");
            Future<DownloadResult> future = executor.submit(task);
            futures.add(future);
        }
        
        // 获取所有下载结果
        System.out.println("\n=== 下载结果汇总 ===");
        for (Future<DownloadResult> future : futures) {
            DownloadResult result = future.get(); // 阻塞等待结果
            System.out.println(result);
        }
        
        executor.shutdown();
    }
}

案例2:售票系统(资源共享)

public class TicketSystem {
    
    // 使用Runnable实现资源共享
    static class TicketSeller implements Runnable {
        private int ticketCount = 100; // 共享的票数
        private final Object lock = new Object();
        
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    if (ticketCount <= 0) {
                        break;
                    }
                    
                    System.out.println(Thread.currentThread().getName() + 
                                     " 卖出第 " + ticketCount + " 张票");
                    ticketCount--;
                    
                    try {
                        Thread.sleep(10); // 模拟卖票耗时
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    public static void main(String[] args) {
        // 创建一个共享的售票任务
        TicketSeller seller = new TicketSeller();
        
        // 创建多个售票窗口(共享同一个seller对象)
        Thread window1 = new Thread(seller, "窗口1");
        Thread window2 = new Thread(seller, "窗口2");
        Thread window3 = new Thread(seller, "窗口3");
        
        window1.start();
        window2.start();
        window3.start();
    }
}

案例3:并行计算(Callable)

import java.util.*;
import java.util.concurrent.*;

public class ParallelCalculator {
    
    // 使用Callable实现带返回值的计算任务
    static class SumTask implements Callable<Long> {
        private long start;
        private long end;
        
        public SumTask(long start, long end) {
            this.start = start;
            this.end = end;
        }
        
        @Override
        public Long call() throws Exception {
            long sum = 0;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            System.out.println(Thread.currentThread().getName() + 
                             " 计算 [" + start + ", " + end + "] = " + sum);
            return sum;
        }
    }
    
    public static void main(String[] args) throws Exception {
        // 计算1到10亿的和,分成10个任务并行计算
        long n = 1_000_000_000L;
        int threadCount = 10;
        long range = n / threadCount;
        
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(threadCount);
        
        // 提交所有子任务
        List<Future<Long>> futures = new ArrayList<>();
        long startTime = System.currentTimeMillis();
        
        for (int i = 0; i < threadCount; i++) {
            long start = i * range + 1;
            long end = (i == threadCount - 1) ? n : (i + 1) * range;
            
            SumTask task = new SumTask(start, end);
            Future<Long> future = executor.submit(task);
            futures.add(future);
        }
        
        // 汇总所有结果
        long totalSum = 0;
        for (Future<Long> future : futures) {
            totalSum += future.get(); // 获取每个子任务的结果
        }
        
        long endTime = System.currentTimeMillis();
        
        System.out.println("\n=== 计算结果 ===");
        System.out.println("1到" + n + "的和为: " + totalSum);
        System.out.println("耗时: " + (endTime - startTime) + "ms");
        
        // 对比单线程计算
        startTime = System.currentTimeMillis();
        long singleSum = 0;
        for (long i = 1; i <= n; i++) {
            singleSum += i;
        }
        endTime = System.currentTimeMillis();
        System.out.println("单线程耗时: " + (endTime - startTime) + "ms");
        
        executor.shutdown();
    }
}

🎯 知识点总结

三种方式核心对比

Thread方式:
优点: 简单直接
缺点: 单继承限制
场景: 简单一次性任务

Runnable方式:
优点: 无继承限制、资源共享、灵活
缺点: 无返回值
场景: 大部分多线程场景(推荐)

Callable方式:
优点: 有返回值、可抛异常
缺点: 代码复杂、获取结果阻塞
场景: 需要返回值的异步任务

最佳实践总结

  1. 优先使用Runnable或Callable
  2. 使用Lambda表达式简化代码
  3. 给线程起有意义的名字
  4. 使用线程池而不是直接创建线程
  5. 必须调用start()而不是run()
  6. 正确处理异常

💡 常见面试题

Q1:创建线程有几种方式?

:主要有三种方式:

  1. 继承Thread类
  2. 实现Runnable接口(推荐)
  3. 实现Callable接口(需要返回值时使用)

Q2:为什么推荐使用Runnable而不是Thread?

  1. Java只支持单继承,实现接口更灵活
  2. Runnable可以方便地实现资源共享
  3. 代码解耦,任务与线程分离
  4. 适合与线程池配合使用

Q3:Runnable和Callable的区别?

  1. 返回值:Callable有返回值,Runnable没有
  2. 异常:Callable可以抛出受检异常,Runnable不能
  3. 方法:Callable是call()方法,Runnable是run()方法
  4. 使用:Callable需要FutureTask包装

Q4:为什么不能直接调用run()方法?

:直接调用run()不会创建新线程,run()会在当前线程中同步执行,失去了多线程的意义。必须调用start()方法,由JVM创建新线程并在新线程中调用run()方法。

Q5:一个线程可以多次调用start()吗?

:不可以。一个线程对象只能调用一次start()方法,第二次调用会抛出IllegalThreadStateException。如果需要重复执行任务,应该创建新的线程对象或使用线程池。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐