【Kill Thread Part.1-1】实现多线程的正确姿势
【Kill Thread Part.1】实现多线程的正确姿势
一、实现多线程的方法是1种还是2种还是4种?
1、Oraclle官网文档正确说法:2种
- 方法一:实现Runnable接口
- 方法二:继承Thread类
2、实现Runnable接口
/**
* 描述:用Runnable方式创建线程
*/
public class RunnableStyle implements Runnable{
public static void main(String[] args) {
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
@Override
public void run(){
System.out.println("用Runnable方法实现线程");
}
}
3、继承Thread类
/**
* 描述:用Thread方式实现线程
*/
public class ThreadStyle extends Thread{
@Override
public void run() {
System.out.println("用Thread类实现线程");
}
public static void main(String[] args) {
new ThreadStyle().start();
}
}
4、两种方法的对比
- 方法1(实现Runnable接口)更好
- 使用Runnable接口后续会有工具类帮助我们管理
- 方法2(继承Thread类)的缺点:
- 业务逻辑,run()方法应该解耦。
- 每次新建一个任务,都需要新建一个线程,需要去创建、执行、销毁。
- Java是单继承的,这个类无法继承其它的类,限制了我们的扩展性。
5、源码分析
- 方法一:
- 实现了Runnable接口的类里,实现了Run方法,如果允许到run()方法,发现target不为空,那么就执行实现类的run方法。
- 方法二:
- 继承Thread类后,重写了run方法,就替代了上图中的run()方法,从而去执行。
二、思考题:同时用两种方法会怎么样?
1、测试代码
/**
* 描述:同时使用两种方法实现线程的方式
*/
public class BothRunnableThread {
public static void main(String[] args) {
//匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我来自Runnable");
}
}) {
//覆盖了Thread中的run方法
@Override
public void run() {
System.out.println("我来自Thread");
}
}.start();
}
}
执行结果:
2、现象解释
我们在创建Thread的时候,重写了run()方法,所以run方法就不存在target的那些代码了。
三、总结:最精确的描述
- 通常我们可以分为两类,Oracle官方文档的描述。
- 准确的讲,创建线程只有一种方式,那就是构造Thread类,而实现线程执行单元却有两种方式:
- 方法一:实现Runnable接口的run方法,并把Runnable实例传给Thread类。
- 方法二:重写Thread的run方法(继承Thread类)
四、典型错误观点分析
1、“线程池”创建线程也算是一种创建线程的方式
①测试代码
/**
* 描述:线程池创建线程的方法
*/
public class ThreadPoll5 {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
executorService.submit(new Task(){});
}
}
}
class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
②源码分析
在Executors源码中,创建线程也是通过new Thread去创建线程。
2、通过Callable和FutureTask创建线程,也算是一种新建线程的方式
实现图,FutureTask也是由Runnable和Thread实现的
3、通过定时器创建也是一种方法
/**
* 描述:定时器创建线程
* 每隔一秒打印当前线程的名字
*/
public class DemoTimmerTask {
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);
}
}
4、匿名内部类实现线程的创建也是一种方法
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();
}
}
5、Lambda表达式方法
public class Lambda {
public static void main(String[] args) {
new Thread( () -> System.out.println(Thread.currentThread().getName())).start();
}
}
五、常见的面试问题
分类:
多线程基础
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?