网站首页 > 文章精选 正文
一.有多少种实现多线程的方法?
1.不同的角度有不同的答案
2.典型的答案有两种,分别是实现Runnable接口和继承Thread类
3.但是看原理,其实Thread类实现了Runnable接口,并且看Thread类的run方法,会发现其两者本质是一样的,run方法发代码如下:
@Override
Public void run(){
if(target != null){
target.run();
}
}
方法1和方法2,也就是说“实现Runnable类”和“继承Thread类然后重写run()”在实现多线程本质上,并没有区别,都最终调用了start()方法来新建线程,这两种方法主要区别在于run()方法来源:方法1:最终调用target.run();
方法2:run()整个都被重写
4.还有其他的实现线程的方法,例如线程池定时器等,他们也能新建线程,但是看源码,从没有逃出本质,也就是实现Runnable接口和继承Thread类。
5.所以总结来说,我们只能通过新建Thread类这一种方式来创建线程,但是类里面的run方法有两种方式来实现,第一种是重写run方法,第二种实现Runnable接口的run方法,然后再把该runnable实例传递给Thread类。除此之外,从表象上看线程池,定时器等工具类也可以创建线程,但是它们的本质都逃不过上面的范围。
二.为什么实现Runnable接口比继承Thread更好?
1.相比较,实现Runnable接口更好,一般都会优先考虑这种发放,除非迫不得已,我们是不考虑继承Thread类的。原因有三点第一点:从代码架构考虑,具体执行的任务,即run方法,它应该和线程创建运行也就是Thread类是接偶的,不应该将两者混为一谈,所以从接偶角度来看,实现Runnable更好。
第二点:如果继承Thread类,每次新建一个任务,只能为此新建一个独立的线程,而新建一个独立的线程的损耗是非常大的,它需要创建然后执行,执行完后需要销毁,而使用Runnable就可以使用线程池之类的工具,就可以大大减小创建销毁线程带来的损耗。
第三点:继承了Thread类后,由于java不支持双继承,不能再继承其他的类,限制了代码的可扩展性。
线程池创建线程:
public class ThreadPool{
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=0; I<100;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());
}
}
定时器创建线程:
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);
}
}
匿名内部类实现线程:
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();
}
Lambda表达式实现线程:
public static void main(String[] args){
new Thread(() -> System.out.println(Thread.currentThread().getName())).start( );
}
如果一个线程两次调用start方法会怎样?为什么?
会抛出一个异常(IllegalThreadStateException),因为在start方法第一行,就会误检查线程的状态,如果不是0,即不是初始状态,就会抛出异常,因为线程状态是不可逆的,所以已经执行过start后,状态改变了,就不能再执行start方法了。
在这里插入图片描述
start()既然也是调用的run()方法,为什么不直接调用run()而是调用start()?
因为调用start()才是真正的去启动一个新线程,会经历线程的各个生命周期,而如果直接调用run()方法,它就是个普通的run()方法,不会用子线程去调用,而直接用主线程执行的。
public static void main(String[] arg){
Runnable runnable = () -> {
System.out.println(Thread.currentThread().start());
};
runnable.run();
new Thread(runnable).start();
}
输出结果:
main
Thread-0
start()执行过程:1.检查线程状态。2.加入线程组。3.调用start0()发放启动线程。start()是被synchronized修饰的,可以保证线程安全。由JVM创建的main线程和system组线程,并不会通过start()来启动。
注意:以上就是创建线程的方法,其中代码不保证可执行性,因为纯手打,有单子字母写错的可能。
猜你喜欢
- 2024-12-27 Python启航:30天编程速成之旅(第23天)- 多线程从入门到精通
- 2024-12-27 10问10答:你真的了解线程池吗? 线程池的用法、原理
- 2024-12-27 线程池最佳线程数量到底要如何配置?
- 2024-12-27 线程池的创建方式,为什么阿里推荐自定义线程池?
- 2024-12-27 如何在C#中创建和使?线程池?请提供?例代码
- 2024-12-27 多线程编程 - 创建线程和结束线程
- 2024-12-27 Java面试篇基础部分-Java创建线程详解
- 2024-12-27 创建线程池有哪几种方式? 44.创建线程池有哪几种方式?
- 2024-12-27 创建线程池的4大方法,7个参数,4种拒绝策略
- 2024-12-27 进程、线程的创建和派生详细过程 线程进程定义
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)