创建线程的四种方式

471 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

创建线程有哪几种方式?

创建线程有四种方式:

  • 继承 Thread 类;
  • 实现 Runnable 接口;
  • 实现 Callable 接口;
  • 使用 Executors 工具类创建线程池继承 Thread 类

步骤

  1. 定义一个Thread类的子类,重写run方法,将相关逻辑实现,run()方法

就是线程要执行的业务逻辑方法

  1. 创建自定义的线程子类对象
  2. 调用子类实例的star()方法来启动线程
1 public class MyThread extends Thread {
2 
3   @Override
4   public void run() {
5   System.out.println(Thread.currentThread().getName() + " run()方法正在执行...");
6   }
7
8 }
1 public class TheadTest {
2
3   public static void main(String[] args) {
4   MyThread myThread = new MyThread();
5   myThread.start();
6   System.out.println(Thread.currentThread().getName() + " main()方法执行结束");
7   }
8
9 }
10

运行结果

1 main main()方法执行结束 
2 Thread‐0 run()方法正在执行... 

实现 Runnable 接口

步骤

  1. 定义Runnable接口实现类MyRunnable,并重写run()方法
  2. 创建MyRunnable实例myRunnable,以myRunnable作为target创建Thead对象,该Thread对象才是真正的线程对象
  3. 调用线程对象的start()方法
1 public class MyRunnable implements Runnable {
2
3   @Override
4   public void run() {
5   System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
6   }
7
8 }
1 public class RunnableTest {
2
3   public static void main(String[] args) {
4   MyRunnable myRunnable = new MyRunnable();
5   Thread thread = new Thread(myRunnable);
6   thread.start();
7   System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
8   }
9
10 }

执行结果

1   main main()方法执行完成
2   Thread‐0 run()方法执行中...

实现 Callable 接口

步骤

  1. 创建实现Callable接口的类myCallable
  2. 以myCallable为参数创建FutureTask对象
  3. 将FutureTask作为参数创建Thread对象
  4. 调用线程对象的start()方法
1 public class MyCallable implements Callable<Integer> {
2
3   @Override
4   public Integer call() {
5   System.out.println(Thread.currentThread().getName() + " call()方法执行中...");
6   return 1;
7   }
8
9 }
1 public class CallableTest {
2
3   public static void main(String[] args) {
4   FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyCallable());
5   Thread thread = new Thread(futureTask);
6   thread.start();
7
8   try {
9   Thread.sleep(1000);
10  System.out.println("返回结果 " + futureTask.get());
11  } catch (InterruptedException e) {
12  e.printStackTrace();
13  } catch (ExecutionException e) {
14  e.printStackTrace();
15  }
16  System.out.println(Thread.currentThread().getName() + " main()方法执行完成");
17  }
18
19 }
​

执行结果

1   Thread‐0 call()方法执行中...
2   返回结果 1
3   main main()方法执行完成

使用 Executors 工具类创建线程池

Executors提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。主要有newFixedThreadPool,newCachedThreadPool,newSingleThreadExecutor,newScheduledThreadPool,后续详细介绍这四种线程池

1 public class MyRunnable implements Runnable {
2
3   @Override
4   public void run() {
5   System.out.println(Thread.currentThread().getName() + " run()方法执行中...");
6   }
7
8 }
1 public class SingleThreadExecutorTest {
2 
3  public static void main(String[] args) { 
4  ExecutorService executorService = Executors.newSingleThreadExecutor(); 
5  MyRunnable runnableTest = new MyRunnable(); 
6   for (int i = 0; i < 5; i++) {
7   executorService.execute(runnableTest);
8   }
9
10  System.out.println("线程任务开始执行");
11  executorService.shutdown();
12  }
13
14 }

执行结果

1   线程任务开始执行
2   pool‐1‐thread‐1 is running...
3   pool‐1‐thread‐1 is running...
4   pool‐1‐thread‐1 is running...
5   pool‐1‐thread‐1 is running...
6   pool‐1‐thread‐1 is running...

线程的 run()和 start()有什么区别?

每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的, run()方法称为线程体。通过调用Thread类的start()方法来启动一个线程。 start() 方法用于启动线程,run() 方法用于执行线程的运行时代码。run() 可以重复调用,而 start() 只能调用一次。 start()方法来启动一个线程,真正实现了多线程运行。调用start()方法无需等待 run方法体代码执行完毕,可以直接继续执行其他的代码; 此时线程是处于就绪状态,并没有运行。 然后通过此Thread类调用方法run()来完成其运行状态, run()方法运行结束, 此线程终止。然后CPU再调度其它线程。

run()方法是在本线程里的,只是线程里的一个函数,而不是多线程的。 如果直接调用run(),其实就相当于是调用了一个普通函数而已,直接待用run()方法必须等待run()方法执行完毕才能执行下面的代码,所以执行路径还是只有一条,根本就没有线程的特征,所以在多线程执行时要使用start()方法而不是run()方法

为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?

这是另一个非常经典的 java 多线程面试问题,而且在面试中会经常被问到。很简单,但是很多人都会答不上来!

new 一个 Thread,线程进入了新建状态。调用 start() 方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。 start() 会执行线程的相应准备工作,然后自动执行 run() 方法的内容,这是真正的多线程工作。

而直接执行 run() 方法,会把 run 方法当成一个 main 线程下的普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。

总结: 调用 start 方法方可启动线程并使线程进入就绪状态,而 run 方法只是 thread 的一个普通方法调用,还是在主线程里执行。

什么是 Callable 和 Future?

Callable 接口类似于 Runnable,从名字就可以看出来了,但是 Runnable 不会返回结果,并且无法抛出返回结果的异常,而 Callable 功能更强大一些,被线程执行后,可以返回值,这个返回值可以被 Future 拿到,也就是说,Future 可以拿到异步执行任务的返回值。

Future 接口表示异步任务,是一个可能还没有完成的异步任务的结果。所以说

Callable用于产生结果,Future 用于获取结果。

什么是 FutureTask

FutureTask 表示一个异步运算的任务。FutureTask 里面可以传入一个

Callable 的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判

断是否已经完成、取消任务等操作。只有当运算完成的时候结果才能取回,如果运算尚未完成 get 方法将会阻塞。一个 FutureTask 对象可以对调用了

Callable 和 Runnable 的对象进行包装,由于 FutureTask 也是Runnable 接口的实现类,所以 FutureTask 也可以放入线程池中。