实现多线程的方法:2种?3种?4种?
Java中有多少种方式实现线程呢?百度一下发现答案不唯一,这就尴尬了。所以本文主要探究实现多线程的具体方式。
抛弃搜索引擎,我们之间看Java的官方文档,可以知道我们有2种方式实现线程。
- 继承Thread类
- 实现Runnable接口
继承Thread类
/**
* 继承Thread实现线程
*/
public class SubThread extends Thread {
@Override
public void run(){
System.out.println("继承Thread实现线程");
}
public static void main(String[] args) {
Thread thread = new SubThread();
thread.start();
}
}
实现Runnable接口
/**
* 实现Runnable接口实现线程
*/
public class BothRunnableThread{
public static void main(String[] args) throws Exception {
new Thread(()-> System.out.println("from Runnable")){
@Override
public void run(){
System.out.println("from thread");
}
}.start();
}
}
选用哪种
推荐实现Runnable接口创建线程。
- 避免了单继承的局限性
一个类只能继承一个类(一个人只能有一个亲爹),类继承了Thread类就不能继承其他的类
实现了Runnable接口,还可以继承其他的类,实现其他的接口
- 增强了程序的扩展性,降低了程序的耦合性(解耦)
实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
实现类中,重写了run方法:用来设置线程任务
创建Thread类对象,调用start:用来开启新线程
底层区别
以上两种实现线程的方式有什么不一样呢?底层实现是什么呢?现在我们来稍微看一下Thread类的源代码吧!
/* What will be run. */
private Runnable target;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
public void run() {
if (target != null) {
target.run();
}
}
可以发现如果Thread的target赋值了就使用Runnable实现类的run方法,否则使用Thread的run方法。那么,如果我们同时使用这两种方式创建线程会如何呢?
public static void main(String[] args) throws Exception {
new Thread(()-> System.out.println("from Runnable")){
@Override
public void run(){
System.out.println("from thread");
}
}.start();
}
/**
* 运行结果为:from thread
* 因为我们重写了Thread的run方法,缺少了
* if (target != null) {
* target.run();
* }
* 所以Runnable实现类的run方法根本不会被使用
*/
其他问题观点
- 线程池创建线程
public class ThreadPoolCreate {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
executorService.submit(new TaskDemo());
}
}
}
class TaskDemo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
没错,线程池可以创建线程共我们使用,但是,我们要揭开表象看本质,默认使用Executors.DefaultThreadFactory
类创建线程,它的底层如下代码所示,r
就是Runnable的实现类。所以本质上还是没脱离以上两种方法。
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0);
...
}
- 通过Callable和FutureTask创建线程
/**
* 通过实现接口,重写call()方法【有返回值】,通过 FutureTask装饰,最终也是需要Thread.start()方法启动线程
*/
public class FutureTaskDemo{
public static void main(String[] args) {
FutureTask futureTask = new FutureTask(new Callable() {
@Override
public Object call() throws Exception {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;
}
});
new Thread(futureTask).start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
通过UML类图不难看出FutureTask本质上还是是实现Runnable接口。
总结
多线程的实现方式很多,但是其本质依旧是继承Thread类和实现Runnable接口。面试回答实现线程的方式,思路如下:
- 从不同角度看有不同的答案
- 官方文档是两种
- 查看其它实现方式发现本质上依旧是上文所提及的两种
- 具体说其它方式
- 结论
版权属于:带翅膀的猫
本文链接:https://www.chengpengper.cn/archives/60/
转载时须注明出处及本声明