线程学习:线程之间的协作

synchronized 避免了线程之间安全问题,能一定的实现线程安全,但有时线程还需要相互协作,此时就需要wait/notify方法。

线程之间的协作应用场景:
生产者/消费者协作模式:这是一种常见的协作模式,生产者线程和消费者线程通过共享队列进行协作,生产者将数据或任务放到队列上,而消费者从队列上取数据或任务,如果队列长度有限,在队列满的时候,生产者需要等待,而在队列为空的时候,消费者需要等待。
同时开始:类似运动员比赛,在听到比赛开始枪响后同时开始,在一些程序,尤其是模拟仿真程序中,要求多个线程能同时开始。
等待结束:主从协作模式也是一种常见的协作模式,主线程将任务分解为若干个子任务,为每个子任务创建一个线程,主线程在继续执行其他任务之前需要等待每个子任务执行完毕。
异步结果:在主从协作模式中,主线程手工创建子线程的写法往往比较麻烦,一种常见的模式是将子线程的管理封装为异步调用,异步调用马上返回,但返回的不是最终的结果,而是一个一般称为Promise或Future的对象,通过它可以在随后获得最终的结果。
集合点:类似于学校或公司组团旅游,在旅游过程中有若干集合点,比如出发集合点,每个人从不同地方来到集合点,所有人到齐后进行下一项活动,在一些程序,比如并行迭代计算中,每个线程负责一部分计算,然后在集合点等待其他线程完成,所有线程到齐后,交换数据和计算结果,再进行下一次迭代。

wait
wait方法是Object方法,有两个。

public final void wait() throws InterruptedException
public final native void wait(long timeout) throws InterruptedException;

主要区别,一个有等待时间,一个没有。
在等待期间,如果线程被中断,则会抛出InterruptedException。

每个对象都有一个锁和等待队列,在代码进入同步块时,获取锁,如果获取不到就会加入等待队列,除此之外,除了用于锁的等待队列,每个对象还有另外一个等待队列,表示条件队列,用于线程之间的协作。调用wait方法就会将当前线程加入条件队列并阻塞,表示该线程不能继续了,需要个等待条件,这个他自己无法改变,需要其他线程改变。而这个改变的方法就是notify和notifyAll方法。

public final native void notify();
public final native void notifyAll();

两者的区别:前者唤醒任意一条线程,后者唤醒所有。

wait/notify方法只能在synchronized代码块内被调用,如果调用wait/notify方法时,当前线程没有持有对象锁,会抛出异常java.lang.IllegalMonitorStateException。

wait方法具体实现过程:
1.调用wait,将线程加入条件队列,释放锁,阻塞等待,此时线程状态编程waiting 或者 timed_waiting
2. 等待时间到,或者被其他线程调用notify /notifyAll唤醒线程,这是要重新竞争锁。
(1) 如果能获取锁,状态变成runnable,并从wait调用中返回。
(2)否则,加入竞争锁的等待队列,状态变成blocked,获取锁后从wait调用中返回。

线程从wait调用中返回后,不代表其等待的条件就一定成立了,它需要重新检查其等待的条件,需要循环调用。

synchronized (obj) {
    while (条件不成立)
        obj.wait();
    ... // 执行条件满足后的操作
}

调用notify会把在条件队列中等待的线程唤醒并从队列中移除,但它不会释放对象锁,也就是说,只有在包含notify的synchronzied代码块执行完后,等待的线程才会从wait调用中返回,而不是执行完notify后马上就 能竞争锁。

在设计多线程协作时,需要想清楚协作的共享变量和条件是什么,这是协作的核心。这个条件变量也是程序自己维护的

wait notify的局限性是只能有一个条件等待队列,这会使条件的分析变得复杂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值