相对于传统的单线程编程,多线程可以在操作系统多核配置的基础上,更好地利用服务器的多个CPU资源,从而使程序更加高效地运行,比如本来一个任务一个线程执行需要100毫秒,现在10个线程执行就只需要10毫秒。下面总结下Java中多线程的知识点:
1、线程的创建方式
Java中多线程的意义在哪?如果直接调用run方法则当前线程一定是等待该方法结束才能调用下面的代码,但是现在是start方法,则此线程等待cpu调用,当此线程获取CPU资源就会变成当前线程,然后就会调用run方法,但其他线程可以不用等这个线程的run方法结束就可以调用下面的方法,显然这些效率大大提高。多线程的原理总结成一句话就是宏观上让线程一起执行靠的是分时利用cpu!(cpu相当于我们人,可以去开启烧水壶的开关后不用等水烧完就可以去开启其他事情的开关。)
四种方式:
(1)继承Thread类
这种方法比较简单,就是先创建一个继承Thread类的NewThread类,然后再new这个类的对象即线程,这个时候线程是新建状态,接着调用start方法来启动这个线程,这个时候线程是就绪状态(除了调用start方法会让线程处于等待状态,还可能是等待或者睡眠好了。),需要注意的是start方法是native方法,存在JVM的本地方法栈,不是用java写的,是操作系统层面的方法,表示在操作系统中开启一个线程,如果要运行这个线程,只需要调用run方法就会执行里面的业务逻辑(注意这个run方法不需要我们手动调用,而是cpu时间片轮到该线程,也就是获取到cpu资源后会自动调用run方法,如果自己手动调用,那就还是当前主线程中使用run这个普通方法,没有达到多线程的目的)。
(2)实现Runnable接口
有了继承Thread类这种方法,为什么还要实现Runnable接口这种方法?
因为java是单继承的,所以如果用继承Thread类来创建线程会出现一个问题:不能继承其他类,比较局限,此时可以采用实现Runnable接口创建线程,需要注意的是其实Thread也是Runnable接口的实现类,但Runnable接口没有start方法。
具体如何实现?
写个实现Runnable接口的NewThread类,再重写run方法,再new一个NewThread类的对象,然后start即可?不可以,因为Runnable接口没有start方法,只能借助Thread类的start方法,所以把这个对象作为Thread类的有参构造创建Thread类的对象(当然也可以像我一开始自己创建实现类,可以直接匿名内部类,省去重新创建一个Runnable实现类),再调用该对象的start方法,因为Thread的底层源码里run方法有个判断,如果目标方法不为空,则调用目标对象的run方法,即这个线程得到了cpu资源就会调用之前Runnable接口中重写的run方法。
(3)实现Callable接口
有了实现Runnable接口这种方法,为什么还要用Callable接口这种方法?
有个场景需要用到Callable接口:需要在一个主线程中开启多个线程并发执行一个任务,然后收集各个线程执行返回的结果并把最终结果汇总起来。
具体如何实现?
A、写一个类实现Callable接口,并重写call方法,在该方法里写计算逻辑并把计算结果返回;
B、创建一个线程池、一个用于接收返回结果的Future List和Callable线程实例;
C、使用线程池提交任务并把线程执行后的结果保存到Future中;
D、在线程执行结束后遍历Future List中的Future对象,然后在该对象上调用get方法就可以获取Callable线程任务返回的数据并汇总结果。
注:A、Runnable接口和Callable接口的区别?
首先是比较是否有返回值?Runnable接口中run方法没有返回值,而Callable中call方法通过泛型规定返回值类型;
其次是比较启动方式不同:二者都不能直接用start方法,因为没有,所以要么靠Thread类的start方法启动,要么靠线程池调用execute或者submit启动,Runnable接口是靠这两种都可以用启动;Callable接口是只能靠线程池的submit启动。
最后比较是否能抛出异常:Runnable接口不能抛出异常,所以不能使用全局容错机制;而Callable接口可以抛出异常,所以可以使用全局容错机制,比如Spring的异常通知。
B、Callable和Future是什么?
Callable和Future都是Executor框架下的接口,也都属于java.util.concurrent包,它们两个是一对组合,Callable用于产生结果,Future用于接收结果,当我们需要获取多线程执行结果的时候就要用到它们,Callable用call()方法执行任务,而且使用泛型定义它的返回值类型,而Future里的get方法获取Callable的执行结果。
C、FutureTask是什么?
FutureTask也可以获取Callable接口任务执行结果,但这个是异步获取,所谓异步是指如果线程执行任务要很久,主线程可以先执行自己的任务再去获取结果。
(4)使用线程池
因为线程是非常宝贵的计算资源,每次使用都创建一次并且在运行结束后都进行销毁很浪费资源,类比数据库的连接池,所以可以使用线程池创建对象实现线程的复用,线程复用的基
Java基础之多线程
最新推荐文章于 2022-10-04 23:08:00 发布
![](https://img-home.csdnimg.cn/images/20240711042549.png)