持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情
创建线程有哪几种方式?
创建线程有四种方式:
- 继承 Thread 类;
- 实现 Runnable 接口;
- 实现 Callable 接口;
- 使用 Executors 工具类创建线程池继承 Thread 类
步骤
- 定义一个Thread类的子类,重写run方法,将相关逻辑实现,run()方法
就是线程要执行的业务逻辑方法
- 创建自定义的线程子类对象
- 调用子类实例的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 接口
步骤
- 定义Runnable接口实现类MyRunnable,并重写run()方法
- 创建MyRunnable实例myRunnable,以myRunnable作为target创建Thead对象,该Thread对象才是真正的线程对象
- 调用线程对象的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 接口
步骤
- 创建实现Callable接口的类myCallable
- 以myCallable为参数创建FutureTask对象
- 将FutureTask作为参数创建Thread对象
- 调用线程对象的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 也可以放入线程池中。