Java并发编程扩展(线程通信、线程池)
之前我说过,实现多线程的方式有4种,但是之前的文章中,我只介绍了两种,那么下面这两种,可以了解了解,不懂没关系。
之前的文章——>Java并发编程之多线程
使用ExecutorService
、Callable
、Future
实现有返回结果的线程
使用ExecutorService
、Callable
、Future
实现有返回结果的线程,也可以叫线程池方式。
ExecutorService
、Callable
、Future
三个接口都是属于**Executor
框架。可返回值的任务必须实现Callable
接口。通过ExecutorService
执行Callable
任务后,可以获取到一个Future
的对象**,在该对象上调用get()
就可以获取到Callable
任务返回的结果了。注意:
Future
的get
方法是阻塞的,即线程无返回结果,get
方法会一直等待。
其他创建线程的方式
除了以上四种主要的线程创建方式之外,也还有很多其他的方式可以启动多线程任务。比如通过Timer启动定时任务,或者通过像Spring Task
和quartz
这样的第三方任务调度框架也可以开启多线程任务,关于第三方任务调度框架的例子还请查询相关资料。
线程通信
什么是线程通信?
当多个线程共同操作共享的资源时,互相告知自己的状态以避免资源争夺。 线程通信主要可以分为三种方式,分别为 共享内存 、 消息传递 和 管道流 。
说白了就是用某些方法来获取线程的资源使用状态!
如何实现线程通信
wait
方法:是Object类的方法,也就是说,所有的对象都有wait
方法,调用了一个对象的wait
方法,获得这个对象锁 的线程将等待,并释放对象锁。
调用对象的
wait
方法时,一定要有一个线程获得对象的对象锁,否则抛出异常。
notify
方法:是Object
类的方法,调用一个对象的notify
方法,在这个对象上等待的线程中随机唤醒一个。
nofifyAll
方法:在这个对象上等待的线程全都唤醒。
sleep
和wait
的区别
-
sleep
是Thread类的方法,wait
是Object类的方法。 -
sleep
不释放对象锁,wait
释放对象锁。 -
sleep
只能到指定的时间自动唤醒,wait
可以指定时间,也可以通过notify
主动唤醒。
线程池
在项目中可能会创建大量的多线程,在执行完多线程后,线程对象被销毁,又有多线程需求时,再次创建多线程, 要反复的创建、销毁线程。可以用线程池管理多线程。
扩展
线程池模式一般分为两种:HS/HA半同步/半异步模式、L/F领导者与跟随者模式。
- 半同步/半异步模式又称为生产者消费者模式,是比较常见的实现方式,比较简单。分为同步层、队列层、异步层三层。同步层的主线程处理工作任务并存入工作队列,工作线程从工作队列取出任务进行处理,如果工作队列为空,则取不到任务的工作线程进入挂起状态。由于线程间有数据通信,因此不适于大数据量交换的场合。
- 领导者跟随者模式,在线程池中的线程可处在3种状态之一:领导者leader、追随者follower或工作者processor。任何时刻线程池只有一个领导者线程。事件到达时,领导者线程负责消息分离,并从处于追随者线程中选出一个来当继任领导者,然后将自身设置为工作者状态去处置该事件。处理完毕后工作者线程将自身的状态置为追随者。这一模式实现复杂,但避免了线程间交换任务数据,提高了CPU cache相似性。在ACE(Adaptive Communication Environment)中,提供了领导者跟随者模式实现。
线程池的伸缩性对性能有较大的影响。
- 创建太多线程,将会浪费一定的资源,有些线程未被充分使用。
- 销毁太多线程,将导致之后浪费时间再次创建它们。
- 创建线程太慢,将会导致长时间的等待,性能变差。
- 销毁线程太慢,导致其它线程资源饥饿。