多线程part3-实现方式
实现方法
①继承Thread类的方式
将一个类声明为Thread
的子类。 这个子类应该重写run
类的方法Thread
。 然后可以分配并启动子类的实例。
例如,计算大于规定值的素数的线程可以写成如下:
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,以下代码将创建一个线程并启动它运行:
PrimeThread p = new PrimeThread(143); p.start();
源码分析:创建Thread的子类对象,在子类对象里重写了Thread父类的run()方法,以子类中的代码为准,不会考虑到Thread实现Runnable方法
Thread父类的run方法,如果没有传入Runnable对象,可以发现实际上没有代码,就是给子类对象重写用的
②实现Runnable接口的方式
创建一个线程是声明实现类Runnable
接口。 那个类然后实现了run
方法。 然后可以分配类的实例,在创建Thread
时作为参数传递,并启动。
这种其他风格的同一个例子如下所示:
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,以下代码将创建一个线程并启动它运行:
PrimeRun p = new PrimeRun(143); new Thread(p).start();
每个线程都有一个用于识别目的的名称。 多个线程可能具有相同的名称。 如果在创建线程时未指定名称,则会为其生成一个新名称。
除非另有说明,否则将null
参数传递给null
中的构造函数或方法将导致抛出NullPointerException
。
实际上该实现方法走的还是Thread的run方法,是一种静态代理设计模式的实现
但,Thread的run()方法如果发现你有传入Runnable对象,那么会优先采用runnable对象的run()方法
③利用Callable接口和Future接口方式
/*
* 多线程的第三种实现方式
* 可以获取多线程运行的结果
* 1.创建一个类MyCallable实现Callable接口
* 2.重写call() 注意拥有返回值,这个返回值就是多线程运行的结果
* 3.测试类中创建MyCallable的对象(多线程执行的任务)
* 4.创建FutureTask的对象(作用管理多线程运行的结果)
* 5.创建Thread的对象(表示线程),并启动
* */
MyCallable mc = new MyCallable();
FutureTask<Integer> ft = new FutureTask<>(mc);
Thread t1 = new Thread(ft);
t1.start();
Integer result = ft.get();System.out.println(result);
用来处理有返回结果的情况,用来获取任务执行的结果,间接实现了Runnable接口
对比
①
优点:编程比较简单,可以直接使用Thread类中的方法
缺点:扩展性较差,不能再继承其他的类
②、③
优点:扩展性强,实现该接口的同时还可以继承其他的类
缺点:编程相对复杂,不能直接使用Thread类中的方法
总结
方法一将线程和任务合在一起,方法二将线程和任务分开
用Runnable更容易与线程池等高级API配合
用Runnable让任务类脱离Thread继承体系,更灵活
我们通过查看Thread和Runnable和Callable可以看到,底层都实现了Runnable方法