浙江众安建设集团有限公司网站百度统计工具
(1)继承 Tread 类
- 继承
Thread
类,创建一个新的线程类 - 重写
run()
方法,将需要并发执行的业务代码编写在run()
方法中
//继承Thread来创建一个线程类
class MyThread extends Thread{@Overridepublic void run(){System.out.println("hello Thread");}public static void main(String[] args) {Thread t=new MyThread();//调用start方法启动线程t.start();}
}
(2)实现 Runnable 接口
将需要异步执行的业务逻辑代码写在Runnable
实现类的run()
方法中,再将Runnable
实例作为target
执行目标传入Thread
实例,其完整步骤如下:
- 定义一个新类实现
Runnable
接口 - 实现
Runnable
接口中的run()
抽象方法,将线程代码逻辑写在该run()
实现方法中 - 通过
Thread
类创建线程对象,将Runnable
实例作为实际参数传递给Thread
类的构造器,由Thread
构造器将该Runnable
实例赋值给自己的target
执行目标属性 - 调用
Thread
实例的start()
方法启动线程 - 线程启动之后,线程的
run()
方法将被JVM
执行,该run()
方法将调用target
属性的run()
方法,从而完成Runnable
实现类中业务代码逻辑的并发执行
//实现Runnable接口。
class MyRunnable implements Runnable{@Overridepublic void run(){System.out.println("hello Thread");}public static void main(String[] args) {Thread t=new Thread(new MyRunnable());t.start();}
}
缺点
- 创建的类不是线程类,而是线程的
target
执行目标类,需要将其实例作为参数传入线程类的构造器,才能创建真正的线程 - 访问当前线程的属性,不能直接访问
Thread
的实例方法,必须通过Thread.currentThread()
获取当前线程实例
优点
- 可以避免由于Java单继承带来的局限性。如果异步逻辑所在类已经继承了一个基类,就没有办法再继承
Thread
类。比如,当一个Dog
类继承了Pet
类,再要继承Thread
类就不行了。所以在已经存在继承关系的情况下,只能使用实现Runnable
接口的方式。 - 逻辑和数据更好分离。通过实现
Runnable
接口的方法创建多线程更加适合同一个资源被多段业务逻辑并行处理的场景。在同一个资源被多个线程逻辑异步、并行处理的场景中,通过实现Runnable
接口的方式设计多个target
执行目标类可以更加方便、清晰地将执行逻辑和数据存储分离,更好地体现了面向对象的设计思想。
继承Thread和实现Runnable区别
- 继承
Thread
类用于多个线程并发完成各自的任务,访问各自的数据资源 - 实现
Runnable
接口用于多个线程并发完成同一个任务,访问同一份数据资源,数据共享资源需要使用原子类型或者进行线程同步控制
(3)实现 Callable 接口:带有返回值
使用Callable
和FutureTask
创建线程的步骤如下:
- 创建一个
Callable
接口的实现类,并实现其call()
方法,编写异步执行的具体逻辑,可以有返回值 - 使用
Callable
实现类的实例构造一个FutureTask
实例 - 使用
FutrueTask
实例作为Thread
构造器的target
入参,构造新的Thread
线程实例 - 调用
Thread
实例的start()
方法启动线程,启动新线程的run()
方法并发执行 - 调用
FutureTask
对象的get()
方法阻塞性地获取并发线程执行结果
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class UseCallable implements Callable<Integer> {@Override//call方法可以抛异常,像线程里面的run方法最多只能在run方法内trycatchpublic Integer call() throws Exception {//所需要实现的功能代码System.out.print("hello Thread");return 1;}public static void main(String[] args) {UseCallable useCallable = new UseCallable();FutureTask<Integer> futureTask = new FutureTask<>(useCallable);new Thread(futureTask).start();try {System.out.print(futureTask.get());}catch(Exception e){}}
}
Callable与Runnable区别
Runnable
的唯一抽象方法run()
没有返回值,也没受检查异常的异常声明,Callable
接口的call()
有返回值,并且声明了受检查异常,功能更强- 使用
Runnable
创建多线程,实现Runnable
接口的实例作为Thread
线程实例的target
来使用 - 使用
Callable
创建多线程,使用RunnableFuture
作为Thread
线程实例的target
实例和获取异步执行的结果,其实现类是FutureTask
(4)线程池创建线程
前面几种方法创建的Thread
实例在执行完成之后是不可复用的,实际工作中需要对已创建好的线程实例进行复用,需要用到线程池。
ExecutorService
是Java
提供的一个线程池接口,每次在异步执行target
目标任务的时候,可以通过ExecutorService
线程池实例去提交或者执行。ExecutorService
实例负责对池中的线程进行管理和调度,并且可以有效控制最大并发线程数,提高系统资源的使用率,同时提供定时执行、定频执行、单线程、并发数控制等功能。
import java.util.concurrent.*;public class MultiThread {private static final int MAX_TURN = 5;private static final int COMPUTE_TIMES = 100000000;private static ExecutorService pool = Executors.newFixedThreadPool(3);static class RunnableTask implements Runnable {@Overridepublic void run() {for (int i = 0; i < MAX_TURN; i++) {System.out.println("多线程执行,第:" + i + "次执行");}}}static class CallableTask implements Callable<Long> {@Overridepublic Long call() throws Exception {long startTime = System.currentTimeMillis();System.out.println("多线程执行,开始时间为:" + startTime);Thread.sleep(1000);for (int i = 0; i < COMPUTE_TIMES; i++) {int j = i * 10000;}long endTime = System.currentTimeMillis();long used = endTime - startTime;System.out.println("多线程执行结束,用时:" + used);return used;}}public static void main(String[] args) throws ExecutionException, InterruptedException {pool.execute(new RunnableTask());pool.execute(new Runnable() {@Overridepublic void run() {for (int i = 0; i < MAX_TURN; i++) {System.out.println("多线程执行,直接实现Runnable");}}});Future<Long> future = pool.submit(new CallableTask());Long result = future.get();System.out.println("异步执行多线程结果为:" + result);}
}
注意:实际开发中不会使用Executors
创建线程池,而是使用ThreadPoolExecutor
的构造方法
execute()与submit()区别
接收的参数不一样
submit()
可以接收两种入参:无返回值的Runnable
类型的target
执行目标实例和有返回值的Callable
类型的target
执行目标实例;
execute()
只接收无返回值的target
执行目标实例或者无返回值的Thread
实例。
submit()有返回值,execute()没有返回值
submit()
方法在提交异步target执行目标之后会返回Future
异步任务实例,以便对target
的异步执行过程进行控制,比如取消执行、获取结果等。
execute()
没有任何返回,target
执行目标实例在执行之后没有办法对其异步执行过程进行控制,只能任其执行,直到其执行结束。