实现多线程的方法到底有1种还是2种还是4种?
Posted 林小鹿@
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现多线程的方法到底有1种还是2种还是4种?相关的知识,希望对你有一定的参考价值。
目录
1、Oracle官网的文档是如何写的?
- 方法一:实现Runnable接口
- 方法二:继承Thread类
实现Runnable接口
package threadcoreknowledge.createthreads;
/**
* 描述: 用Runnable方式创建线程
*/
public class RunnableStyle implements Runnable
public static void main(String[] args)
Thread thread = new Thread(new RunnableStyle());
thread.start();
public void run()
System.out.println("用Runnable方式创建线程");
继承Thread类
package threadcoreknowledge.createthreads;
/**
* 描述: 用Thread方式实现线程
*/
public class ThreadStyle extends Thread
@Override
public void run()
System.out.println("用Thread方式实现线程");
public static void main(String[] args)
new ThreadStyle().start();
2、两种方法的对比
方法1(实现Runnable接口)更好。
方法2的缺点:
- 从代码的架构来考虑,具体执行的任务(run方法里面的内容)应该是和Thread解耦的,不应该把这两件事情混为一谈;
- 从资源的节约上,继承了 Thread 类,每次我们想新建一个任务只能去新建一个独立的线程,而新建一个独立的线程损耗是十分大的(因为需要去创建,销毁);
- 继承了Thread 类,由于Java不支持双继承,导致这个类无法去继承其他类了,这大大限制了代码的可扩性。
两种方法的本质对比
- 方法一:最终调用target.run();
@Override
public void run()
if (target != null)
target.run();
- 方法二:run()整个都被重写
3、思考题:同时用两种方法会怎么样?
package threadcoreknowledge.createthreads;
/**
* 描述 同时使用Runnable和Thread两种实现线程的方式
*/
public class BothRunnableThread
public static void main(String[] args)
new Thread(new Runnable() //传入Runnable对象
@Override
public void run()
System.out.println("我来自Runnable");
)
@Override //重写run()方法
public void run()
System.out.println("我来自Thread");
.start();
代码执行结果:
从面向对象的思想去考虑: 因为我们重写了run()方法,所以导致Thread的三行run()方法代码不再存在,即使传入传入Runnable对象,它也不再执行。
4、总结:最精准的描述
1、通常我们可以分为两类,Orracle也是这么说的
2、准确的讲,创建线程只有—种方式那就是构造Thread类,而实现线程的执行单元有两种方式
- 方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类;
- 方法二:重写Thread的run方法(继承Thread类)。
5、典型错误观点分析
1、“线程池创建线程也算是一种新建线程的方式”
package threadcoreknowledge.wrongways;
import javafx.concurrent.Task;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPools
public static void main(String[] args)
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++)
executorService.submit(new Tasks()
);
class Tasks implements Runnable
@Override
public void run()
try
Thread.sleep(500);
catch (InterruptedException e)
e.printStackTrace();
System.out.println(Thread.currentThread().getName());
代码执行结果:
public Thread newThread(Runnable r)
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
点进去源码可以看到,线程池本质创建线程的方法是new Thread
,因此这并不是一种新的创建线程方式 。
2、“通过Callable和FutureTask创建线程,也算是一种新建线程的方式”
本质是实现Runnable接口和继承Thread类实现的。
3、“无返回值是实现runnable接口,有返回值是实现callable接口,所以callable是新的实现线程的方式”
本质依旧是实现Runnable接口和继承Thread类实现的。
4、定时器
package threadcoreknowledge.wrongways;
import java.util.Timer;
import java.util.TimerTask;
/**
* 描述: 定时器创建线程
*/
public class DemoTimerTask
public static void main(String[] args)
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask()
@Override
public void run()
System.out.println(Thread.currentThread().getName());
,1000,1000);
代码执行结果:
5、匿名内部类
package threadcoreknowledge.wrongways;
public class AnonymousInnerClassDemo
public static void main(String[] args)
new Thread()
@Override
public void run()
System.out.println(Thread.currentThread().getName());
.start();
new Thread(new Runnable()
@Override
public void run()
System.out.println(Thread.currentThread().getName());
).start();
代码执行结果:
本质一样。
6、Lambda表达式
package threadcoreknowledge.wrongways;
public class Lambda
public static void main(String[] args)
new Thread(()-> System.out.println(Thread.currentThread().getName())).start();
代码执行结果:
本质一样。
6、典型错误观点总结
多线程的实现方式,在代码中写法千变万化,但其本质万变不离其宗。他们通过各种各样的包装,比如线程池,定时器,包装的外表好像是实现线程池的一种方式,但是我们打开包装透过源码去看到他最终实现的道理的话,本质其实是实现Runnable接口和继承Thread类。
7、实现多线程——常见面试问题
有多少种实现线程的方法?思路有5点:
- 从不同的角度看,会有不同的答案。
- 典型答案是两种
- 我们看原理,两种本质都是一样的
- 具体展开说其他方式
- 结论
实现Runnable接口和继承Thread类哪种方式更好?
- 从代码架构角度
- 新建线程的损耗
- Java不支持双继承
以上是关于实现多线程的方法到底有1种还是2种还是4种?的主要内容,如果未能解决你的问题,请参考以下文章