程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

Java创建线程的五种方式

balukai 2025-01-02 14:37:16 文章精选 7 ℃

创建线程的五种方式

  1. 继承Thread类
  2. 实现Runnable接口
  3. 使用Callable和Future
  4. 使用Lambda表达式
  5. 使用线程池

继承Thread类创建线程

继承 Thread 类创建线程的步骤如下:

  1. 创建一个继承 Thread 类的子类。
  2. 重写 Thread 类的 run() 方法。
  3. 在 run() 方法中编写线程要执行的任务。
  4. 创建 Thread 子类的对象。
  5. 调用 Thread 子类对象的 start() 方法来启动线程。

以下是一个继承 Thread 类创建线程的示例:

public class MyThread extends Thread {

    @Override
    public void run() {
        // 在 run() 方法中编写线程要执行的任务。
        System.out.println("Hello, world!");
    }

}

要启动 MyThread 线程,可以使用以下代码:

MyThread myThread = new MyThread();
myThread.start();

当 MyThread 线程启动后,它将执行 run() 方法中的代码,并在控制台上输出 "Hello, world!"。

实现Runnable接口

实现 Runnable 接口创建线程的步骤如下:

  1. 创建一个实现 Runnable 接口的类。
  2. 在 Runnable 接口的 run() 方法中编写线程要执行的任务。
  3. 创建 Runnable 接口的实现类的对象。
  4. 将 Runnable 接口的实现类的对象传递给 Thread 类的构造方法来创建 Thread 对象。
  5. 调用 Thread 对象的 start() 方法来启动线程。

以下是一个实现 Runnable 接口创建线程的示例:

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        // 在 run() 方法中编写线程要执行的任务。
        System.out.println("Hello, world!");
    }

}

要启动 MyRunnable 线程,可以使用以下代码:

Thread myThread = new Thread(new MyRunnable());
myThread.start();

当 MyRunnable 线程启动后,它将执行 run() 方法中的代码,并在控制台上输出 "Hello, world!"。

使用Callable和Future

Callable 和 Future 接口用于异步执行任务。Callable 接口用于创建可以返回结果的任务,Future 接口用于获取任务的结果。

Callable 接口的实现类必须实现 call() 方法,call() 方法用于执行任务并返回结果。Future 接口的实现类必须实现 get() 方法,get() 方法用于获取任务的结果。

Callable 和 Future 接口通常用于与线程池一起使用。线程池可以用于管理多个线程,并可以将任务提交给线程池以执行。当任务执行完成后,线程池会返回 Future 对象,Future 对象可以用于获取任务的结果。

以下是一个使用 Callable 和 Future 接口的示例:

ExecutorService executorService = Executors.newFixedThreadPool(10);

// 创建 Callable 对象
Callable<Integer> callable = new Callable<Integer>() {

    @Override
    public Integer call() throws Exception {
        // 执行任务
        System.out.println("任务正在执行...");
        Thread.sleep(1000);
        return 100;
    }

};

// 将任务提交给线程池
Future<Integer> future = executorService.submit(callable);

// 获取任务的结果
try {
    Integer result = future.get();
    System.out.println("任务执行完成,结果为:" + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

// 关闭线程池
executorService.shutdown();

当程序执行完毕后,控制台将输出以下内容:

任务正在执行...
任务执行完成,结果为:100

当然我们可以不用线程池来实现:

Callable<Integer> callable = new Callable<Integer>() {
   public Integer call() throws Exception {
      // 线程的执行逻辑
      System.out.println("线程执行逻辑");
      return 100;
   }
};

FutureTask<Integer> futureTask = new FutureTask<>(callable);
//因为future也是实现了runnable接口的
Thread thread = new Thread(futureTask);
thread.start();

try {
   Integer result = futureTask.get();
   System.out.println("任务执行完成,结果为:" + result);
} catch (InterruptedException | ExecutionException e) {
   e.printStackTrace();
}

Callable 接口提供了一个 call() 方法,用于执行任务并返回结果。FutureTask 类是 Future 和 Runnable 接口的实现类,它保存着 call() 方法和实现了 Runnable 的 run() 方法,还有线程的状态和返回值变量,在调用run()方法的时候会执行call()。

当调用 FutureTask.get() 方法时,FutureTask 会先判断线程是否执行完成。如果线程已经执行完成,则 FutureTask 会返回线程的返回值。如果线程还没有执行完成,则 FutureTask 会调用 park() 方法使当前线程阻塞,直到线程执行完成后被唤醒。

FutureTask简化实现如下,源码是比这个要负责的。

public class FutureTask<V> implements RunnableFuture<V> {
 // 任务是否已经执行完成
    private volatile boolean done; 
    // 任务的返回值
    private volatile V result; 
 // 构造函数
    public FutureTask(Callable<V> callable) { 
        this.callable = callable;
    }

    public FutureTask(Runnable runnable, V result) { 
        this.runnable = runnable;
        this.result = result;
    }

    @Override
    public void run() { // 任务的执行方法
        if (runnable != null) { // 执行Runnable任务
            runnable.run();
        } else { // 执行Callable任务
            try {
                result = callable.call();
            } catch (Exception e) { // 捕获异常
                setException(e);
            }
        }
        done = true; // 任务执行完成
        unparkSuccessor(); // 唤醒线程
    }

    @Override
    public boolean isDone() { // 判断任务是否已经执行完成
        return done;
    }

    @Override
    public V get() throws InterruptedException, ExecutionException { // 获取任务的返回值
        if (done) { // 任务已经执行完成
            return result;
        }
        park(); // 阻塞当前线程
        return result; // 获取任务的返回值
    }

    @Override
    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { // 获取任务的返回值,带有超时时间
        if (done) { // 任务已经执行完成
            return result;
        }
        park(unit.toNanos(timeout)); // 阻塞当前线程,指定超时时间
        return result; // 获取任务的返回值
    }

    private void park() { // 阻塞当前线程
        if (Thread.currentThread() != this) { // 当前线程不是FutureTask的执行线程
            LockSupport.park(this); // 调用park()方法使当前线程阻塞
        }
    }

    private void unparkSuccessor() { // 唤醒线程
        if (successor != null) { // 后继线程不为null
            LockSupport.unpark(successor); // 唤醒线程
        }
    }
}

使用Executor框架

使用Executor框架可以更方便地管理和调度线程。通过使用ExecutorService接口的实现类(如ThreadPoolExecutor),可以创建线程池,并将任务提交给线程池来执行。让我们看看下面的示例:

ExecutorService executor = Executors.newFixedThreadPool(5);

// 创建Runnable对象
Runnable runnable = () -> {
    // 线程的执行逻辑
    System.out.println("线程执行逻辑");
};

// 提交任务给线程池
executor.submit(runnable);

// 关闭线程池
executor.shutdown();

使用Lambda表达式

在Java 8及更高版本中,可以使用Lambda表达式来简化线程的创建和执行。通过将逻辑代码封装在Lambda表达式中,你可以更加简洁地创建线程。让我们看看下面的示例:

Thread thread = new Thread(() -> {
    // 线程的执行逻辑
    System.out.println("线程执行逻辑");
});

// 启动线程
thread.start();

总结

继承 Thread 类是最简单的创建线程的方式,但是需要重写 Thread 类的 run() 方法,并在 run() 方法中编写线程要执行的任务。

实现 Runnable 接口是创建线程的另一种方式,比继承 Thread 类更灵活,可以将线程要执行的任务封装在 Runnable 对象中,然后将 Runnable 对象传递给 Thread 类的构造方法,Thread 类会自动执行 Runnable 对象的 run() 方法。

Callable 接口和 Future 接口用于创建可以返回结果的线程。Callable 接口的实现类必须实现 call() 方法,call() 方法用于执行任务并返回结果。Future 接口用于获取任务的结果。

创建线程方法,说几种都可以,按照jdk源码注释上有写是两种,是继承Thread和实现Runnable。说一种也行,因为最后都是通过thread.start()来创建线程,等待cpu调度最后执行run()方法。其他的那几种到最后也就是上边两种方式的实现而已。

Tags:

最近发表
标签列表