Java[学习总结]-多线程(七)之线程之间的协作

1.wait()

1.1 概念
方法wait()的作用是使当前执行代码线程进行等待,是Object类的方法,该方法用来将当前线程置于“阻塞队列”中,并在wait()所在的代码处停止执行(被挂起),直到接到通知被唤醒。

1.2要求
在调用wait()之前,线程必须持有该对象的对象级别锁,只能在同步方法或者是同步块中调用此方法。在执行wait方法后,当前线程释放锁。会和其他线程竞争重新获得锁。如果调用wait方法没有持有锁,则会抛出异常。

2.notify()

2.1 概念
该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随便选择一个呈wait状态的线程。在执行notify方法后,当前线程不会马上释放该对象锁,要等到执行notify方法的线程将程序执行完才能会释放锁。
注意:实际上相当于只是唤醒被挂起了线程,顺序执行完当前线程的代码,它依旧会释放锁。

2.2 执行要求
方法notify()也是在同步方法或者同步块中调用,调用前同样要获得对象的对象级别所,否则抛出异常。

2.3 notifyAll()
方法notifyAll()方法可以使正在等待队列中等待同一共享资源的”全部”线程从等待状态退出,进入可运行状态。

3.消费者/生产者模式(多对多)

(1)要求创建一个生产者,一个消费者,多个生产者线程,多个消费者线程,测试程序有多个生产者和消费者交替运行。
(2)这种情况下容易出现“假死”,即所有的线程都出现WAITING状态。原因是在调用notify方法的时候可能连续唤醒的是同类。
(3)Wait条件由if修改为while
(4)Notify方法修改为notifyAll()方法

4.使用条件变量协调线程

4.1 condition类
在没有使用synchronization关键字保证同步,而采用Lock的程序。Java提供了condition类来保持协调。Lock替代synchronization,Condition替代同步监视器功能。
(1)await:类似于wait。
(2)signal:唤醒在此Lock对象上等待的单个线程,选择是任意的。
(3)signalAll:唤醒在此Lock对象上等待的所有线程,选择是任意的。

4.2 有选择地通知唤醒
(1)使用notify()/notifyAIl()方法进行通知时,被通知的线程却是JVM随机选择的。当notifyAll()通知所有WAITING线程,没有选择权,会出现相当大的效率问题。
但是ReentrantLock结合Condition类可以”选择性通知”。Synchronized就相当于整个Lock对象中只有一个Condition对象,所有线程都注册到它的一个对象上。

4.3 使用方式
Condition可以实现唤醒部分指定线程,这样有助于程序运行的效率。先定义多个Condition对象,再对线程进行分组, 然后唤醒指定组中的线程。

5.使用阻塞队列(BlockingQueue)控制线程通信

5.1 概念
JDK5提供 了一个BlockingQueue接口,其主要作用不是作为容器,而是线程同步的工具。它具有一个特征:当生产线者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程被阻塞;当消费者线程试图从BlockingQueue中取出元素时,如果该队列已空,则该线程被阻塞。这样可以很好的控制线程的通信。
在这里插入图片描述

public class BlockingQueueTest
public static void main(String[] args) throws Exception{
//定义一个长度为2的阻塞队列
BlockingQueue<String> bq = new ArrayBlockingQueue<>(2);
bq.put("Java"); //与bq.add("Java")、bq.offer("Java")相同
bq.put("Java"); //与bq.add("Java")、bq.offer("Java")相同
}

在这里插入图片描述5.2 类队列
(1)ArrayBlockingQueue:基于数组实现的BlockingQueue队列
(2)LinkedBlockingQueue:基于链表实现的BlockingQueue队列
(3)PriorityBlockingQueue:它并不标准的阻塞队列。该队列取元素时,并不是取出队列中存在时间最长的元素,而是队列中最小的元素。判断元素的大小即可根据元素的来自然排序(实现Comparable接口)。
(4)SynchronousQueue:同步队列。对该队列的存、取操作必须交替进行
(5)DelayQueue:它是一个特殊的BlockingQueue队列,底层基于PriorityBlockingQueue实现,不过,DelayQueue要求集合元素都实现Delay接口。根据集合元素的getDelay0方法的返回值进行排序。

6.无限制创建线程的不足

(1)
在生产环境中,“ 为每个任务分配一个线程”这种方法存在一些缺陷:尤其是当需要创建大量的线程时:线程的生命周期的开销非常高:线程的创建与销毁并不是没有代价的。创建过程中需要时间,延迟处理请求,并且需要JVM和操作系统提供–些辅助操作。如果请求达到率非常高且请求的处理过程是轻量级的,那么为每个请求创建一个新线程将消耗大量的计算资源。
(2)
资源消耗:活跃的线程会消耗系统资源,尤其是内存。如果可运行的线程数量多于可处用处理器的数量,那么有些线程将闲置。大量空闲线程会占有许多内存,给垃圾回收器带来压力,而且馱量线程在竞争CPU资源时还将产生其他的性能开销。如果你拥有足够多的线程使所有CPU保持忙碌状态,那么再创建更多的线程反而会降低性能。
(3)
稳定性:在可创建线程的数量上存在一个限制。这个限制将随着平台的不同而不同,并且受多个因素制约,包括JVM的启动参数、Thread构造函数中请求栈的大小,以及底层操作系统对线程的限制等。如果破坏了这些限制,那么很可能抛出OutOfMemeoryError异常。最简单的方法就是通过构造程序避免出现这些限制。在一定的范围内,增加线程可以提高系统的吞吐率,但如果超出了这个范围,再创建更多的线程只会降低程序的执行速度。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值