关于LuacoroutinevsJavawait/notify的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于(三)java多线程之waitnotifynotifyAll、c++11Con
关于Lua coroutine vs Java wait/notify的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于(三)java多线程之wait notify notifyAll、c++11 Condition Variable notify_one notify_all wait、java condition await signal signalall对比wait notify notifyall、java notify,wait,notifyAll理解和实例(一)等相关知识的信息别忘了在本站进行查找喔。
本文目录一览:- Lua coroutine vs Java wait/notify
- (三)java多线程之wait notify notifyAll
- c++11 Condition Variable notify_one notify_all wait
- java condition await signal signalall对比wait notify notifyall
- java notify,wait,notifyAll理解和实例(一)
Lua coroutine vs Java wait/notify
在上文Lua coroutine 不一样的多线程编程思路 中想到coroutine的运行机制跟Java中的wait/notify很相似,所以写了一个简单程序比较一下。
源代码
Lua code
co = coroutine.create(function(loops) for i = 1,loops do coroutine.yield() end end) local x = os.clock() local loops = 100 * 1000 * 1000 coroutine.resume(co,loops) for i = 1,loops do coroutine.resume(co) end print(string.format("elapsed time: %.2f\n",os.clock() - x))
Java code
public class TestWait { public static void main(String[] args) { WaitClass wc = new WaitClass(); wc.start(); int loops = 100 * 1000 * 1000; long t1 = System.currentTimeMillis(); for (int i = 0; i < loops; i++) { synchronized (wc) { wc.notify(); } } long t2 = System.currentTimeMillis(); System.out.println("elapsed time: " + (t2 - t1) / 1000l); } } class WaitClass extends Thread { public void run() { while (true) { synchronized (this) { try { this.wait(); } catch (InterruptedException e) { e.printstacktrace(); } } } } }
运行结果
Lua elapsed time: 53.36
Java elapsed time: 51
cpu占用
运行环境:4core XEON
Lua 1cpu 100%,其他cpu0%,total 25% (其中cpu sys 0%)
Java 2个cpu 40%-50%,其他cpu 0%,total 25% (其中cpu sys 5%-10%)
从结果看,coroutine只利用了一个cpu,和原理所说完全一致。
Java利用了2个cpu,各占用了50%的cpu时间运行和50%的时间等待,和设计也一致。另外Java用了5-10%的sys cpu时间用于线程context switch
结论
虽然这两种程序没有直接可比性,但仍然可以看出一些有趣的地方:
- Lua虽然在各种性能评比中performance比Java低一个数量级,但在这个场景中也跑平了Java
- Java为了调用notify/wait,用了同步锁,因此测试场景对Java不利。
再谈coroutine应用场景
今天又看到qiezi的文章并发编程模型:Thread,Coroutine,Callback … 分析得很深入,对这方面感兴趣的可以进一步去深入了解。
另外qiezi在Coroutine在并发程序中的应用中提到四种场景,可以理解是对我上篇文章对coroutine应用场景的一种答案。
- 状态机。
- 异步IO操作:异步IO操作通常是发起一个IO请求,由操作系统完成以后回调指定的方法或者使用其它方式通知。
- 高并发网络服务器,高并发服务器除了要处理场景一的情况外,可能还要结合场景二,多线程方案有时候完全不能接受,更多的是基于事件、异步IO或者是混合事件和多线程的模型。
- 客户端并发应用
但是我还是觉得存在疑虑,后面几种我觉得用多线程/线程池模式也可以很好解决。其实select/epoll异步IO方式跟多线程并不矛盾。多线程并不代表每个线程需要recv阻塞在那里。目前网络服务器的多线程通常是指业务逻辑处理部分使用多线程。比如Java中用mina来处理连接(相当于epoll),mina在收到数据包之后再分发给负责业务逻辑的thread pool处理。如果是cpu密集型任务,简单把线程池的线程数设成cpu数即可达到性能最佳。这时如果把线程数设成1,就很类似coroutine模式了。而Java模式所增加的消耗,主要是new runnable class以及线程池调度的开销。
(三)java多线程之wait notify notifyAll
本人邮箱: <kco1989@qq.com>
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载
引言
今天我打算讲一下Object.wait
,Object.notify
,Object.notifyAll
这三个方法. 首先我们查看一下api看看,官方api对这几个方法的介绍.
理论
Object.wait()
: 导致当前线程一直等待,直到另一外一个线程用同一个对象调用Object.notify
或Object.notifyAll
方法.换种说法,就是调用Object.wait()
和调用Object.wait(0)
行为是一致的.
在调用wait
时,必须要有调用对象的锁.而这个锁会一直等待直接这个对象调用notify
或notifyAll
方法才被释放.这是等待的线程才能重新获取对象的锁而继续执行.
这里需要防止中断或假唤醒线程,所以一般在调用wait
会使用循环,比如
synchronized (obj) {
while (当条件不满足时)
obj.wait();
... // Perform action appropriate to condition
}
Object.notify
/Object.notifyAll
唤醒一个拥有用到当前对象的锁且在等待的线程.如果有多个等待的线程,则随机唤醒一个.而被唤醒的线程会直到当前线程释放对象的锁后才被执行.被唤醒的线程会使用竞争的方式去获取对象的锁.这些被唤醒的线程都是一视同仁,不会被区别对待.所以每个线程获取对象的锁的几率都是平等的,也是随机的.
总之,在同一个时间点只能有一个线程拥有对象的锁.
上面的解释可能比较官方,出现什么对象的锁
,唤醒
等待之类的时.初学者可能还不能很好的理解.现在我还是用上篇文章的比喻解释一下.
比如现在有一群小明
/''小红''都想进入卧室,卧室的门只有一个,钥匙也只有一把,每次也只能进入一个''小明''和一个小红
.(多了就...)这时候卧室里面如果只有有一个人,那么他/她就只能等待,然后释放卧室门的锁.然后唤醒另外一个她/他进来.进来后把卧室门给锁了,等小明
和小红
谈完人生和理想的时候.再通知其他人,告诉他们房间是空的,可以进来了.这是另外的一群小明
/小红
就会争先恐后的进来.但还是只能进一个小明
和一个小红
.(这里不理解,没有关系,看完文章之后,再回过头看一下这里,说不定就会豁然开朗了.)
这些人比喻就是上面讲到的
线程
,卧室就相当于上面说的对象
,门就相当于上面的锁
,这样一比喻是不是好理解一点.需要注意的是卧室是特定的一个,即每个人等待的卧室都必须是同一个.这样才能用那把钥匙去开门.
编码
如果有些人还不能理解,那么这里我就写一个程序再来解析一下.就用上面的比喻做例子吧.ok,开始编码.
首先,我们要先定一个抽象卧室类
Room
,这个类需要用成员变量gril
,还必须提供两个方法makeGridInRoom(String gril)
和playWithGril(String boy)
,这个类的实现我们暂时先放放,后面再说之后,我们要创建中间人来一个找小妞的类,
GrilProduct
,把卧室room
传进来,然后依此找到十个小妞进入卧室里.
public class GrilProduct implements Runnable{
private Room room;
public GrilProduct(Room room) {
this.room = room;
}
@Override
public void run() {
for (int i = 0; i < 10; i ++){
room.makeGridInRoom("小红" + i + "号");
}
}
}
然后,我们还需要有一个花花公子类,来和小妞谈谈人生,说说理想,
PlayBoy
public class PlayBoy implements Runnable{
private Room room;
private String boy;
public PlayBoy(Room room, String boy) {
this.room = room;
this.boy = boy;
}
@Override
public void run() {
room.playWithGril(boy);
}
}
最后,我们要让那个中间人活动起来,而且需要来依此十个花花公子来和小妞谈人生,谈理想.毕竟小妞一个人也只能应付一个花花公子,加班是很累的.
TestMain
public class TestMain {
public static void main(String[] args) {
Room room1 = new Room1();
playGameInRoom(room1);
// Room room2 = new Room2();
// playGameInRoom(room2);
//
// Room room3 = new Room3();
// playGameInRoom(room3);
//
// Room room4 = new Room4();
// playGameInRoom(room4);
}
public static void playGameInRoom(Room room){
Thread grilProduct = new Thread(new GrilProduct(room));
Set<Thread> boyThread = new HashSet<>();
for (int i = 0; i < 10; i ++){
boyThread.add(new Thread(new PlayBoy(room, "小明" + i + "号")));
}
grilProduct.start();
for (Thread boy : boyThread){
boy.start();
}
}
}
行了,现在开始来考虑Room
要怎么写.
第一个想法,比较简单,小妞来了就来了,花花公子来了就开始谈人生,谈理想.编写
Room1
继承Room
public class Room1 extends Room{
private String gril;
@Override
public synchronized void makeGridInRoom(String gril){
this.gril = gril;
}
@Override
public synchronized void playWithGril(String boy){
System.out.println(boy + " play with " + this.gril);
this.gril = null;
}
}
运行一下结果:
小明1号 play with 小红9号
小明3号 play with null
小明6号 play with null
小明0号 play with null
小明8号 play with null
小明5号 play with null
小明7号 play with null
小明2号 play with null
小明4号 play with null
小明9号 play with null
我靠,怎么只有小明1号
和小红9号
聊上了,其他公子自己玩. 哦,原来是小红9号
把前面几个给挤掉了,其他小明
,小红
不服了,凭什么小红9号
那么霸气.行,为了满足大家,继续改..
第二个想法,如果花花公子来了,但小妞不在卧室,那么公子就会离开;另外如果一个小妞发现卧室有人,她也选择离开.
public class Room2 extends Room{
@Override
public synchronized void makeGridInRoom(String gril){
if (this.gril != null){
return;
}
this.gril = gril;
}
@Override
public synchronized void playWithGril(String boy){
if (this.gril == null){
return;
}
System.out.println(boy + " play with " + this.gril);
this.gril = null;
}
}
运行结果是
小明1号 play with 小红0号
而且每次运行结果都不一样,这时候其他人不爽了,为什么我进入的时候没人呢?
这里我们需要改变一下策略.如果卧室没人.那么让他们就睡一下,等待其他人来谈完人生再走.
public class Room3 extends Room{
@Override
public synchronized void makeGridInRoom(String gril){
while (this.gril != null){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.gril = gril;
}
@Override
public synchronized void playWithGril(String boy){
while (this.gril == null){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(boy + " play with " + this.gril);
this.gril = null;
}
}
运行结果.我靠,卧室被锁了,而且只有一个人一直在里面孤零零的等着,永远等不到另外一个人来.悲剧了.....
不行,我们还是要改变一下策略.如果花花公子发现卧室里没有小妞,那就先等着.等待小妞来了,让她来叫醒我.小妞也是一样,如果卧室里已经有小妞了,那么就先等着,等他们谈完人生,谈完理想再来叫醒我.
public class Room4 extends Room{
@Override
public synchronized void makeGridInRoom(String gril){
while (this.gril != null){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.notifyAll();
this.gril = gril;
}
@Override
public synchronized void playWithGril(String boy){
while (this.gril == null){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(boy + " play with " + this.gril);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.notifyAll();
this.gril = null;
}
}
运行结果:
小明9号 play with 小红0号
小明1号 play with 小红1号
小明4号 play with 小红2号
小明3号 play with 小红3号
小明2号 play with 小红4号
小明6号 play with 小红5号
小明7号 play with 小红6号
小明0号 play with 小红7号
小明5号 play with 小红8号
小明8号 play with 小红9号
谢天谢地啊,每个小明
都找到小红
了,我再也不用担心被小明
/小红
在k死了.
后记
在最后两种做法,一种是调用
Thread.sleep
,一种是调用Object.wait
.两种都是是线程睡眠等待,但为什么结果不一样呢?这里解释一下,Thread.sleep
会释放资源,但是不会释放锁,所以如果有人进入卧室,就一直霸占这这个卧室,其他压根都进不来.但Object.wait
除了会释放资源之外,还会释放锁.等到小红
/小明
进入卧室,在通知小明
/小红
进入卧室.还有使用
Object.notifyAll
和Object.notify
,我自己觉得在可以使用Object.notify
的地方,都可以使用Object.notifyAll
代替,但是可以使用Object.notifyAll
却不能使用Object.notify
代替. 在上述的例子,如果只有一个中间人GrilProduct
只找一个小红
,而且也只有一个花花公子PlayBoy
,那么使用Object.notify
和Object.notifyAll
都一样,但是如果有多个花花公子,就必须使用Object.notifyAll
去唤醒所有人,让其中一个人跑进卧室里.其他人进不去,就继续等待.然后继续全部唤醒,继续等待....Object.wait()
和Object.wait(long)
的区别,wait()
是进入卧室之后就一直等,直到被唤醒,wait(long)
则是进入卧室之后一直等或者到了指定时间内被唤醒
对上面的例子改写一下,在GrilProduct
改为每500ms才找到一个小红
public class GrilProduct implements Runnable{
private Room room;
public GrilProduct(Room room) {
this.room = room;
}
@Override
public void run() {
for (int i = 0; i < 10; i ++){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
room.makeGridInRoom("小红" + i + "号");
}
}
}
然后对Room4.playWithGril
改为
@Override
public synchronized void playWithGril(String boy){
while (this.gril == null){
try {
System.out.println(boy + " 我的心在等待,永远在等待...");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(boy + " play with " + this.gril);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.notifyAll();
this.gril = null;
}
运行一下
小明1号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明9号 play with 小红0号
小明4号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明1号 我的心在等待,永远在等待...
小明1号 play with 小红1号
小明6号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明4号 play with 小红2号
小明2号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明6号 play with 小红3号
小明3号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明2号 play with 小红4号
小明7号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明3号 play with 小红5号
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明7号 play with 小红6号
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明5号 play with 小红7号
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明8号 play with 小红8号
小明0号 我的心在等待,永远在等待...
小明0号 play with 小红9号
然后在对Room4.playWithGril
改写为
@Override
public synchronized void playWithGril(String boy){
while (this.gril == null){
try {
System.out.println(boy + " 我的心在等待,永远在等待...");
this.wait(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(boy + " play with " + this.gril);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.notifyAll();
this.gril = null;
}
然后再运行一下
小明3号 我的心在等待,永远在等待...
小明1号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明1号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明1号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明1号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明1号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明1号 我的心在等待,永远在等待...
小明1号 play with 小红0号
小明7号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明3号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明3号 play with 小红1号
小明6号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明4号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明4号 play with 小红2号
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明8号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明8号 play with 小红3号
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明6号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明6号 play with 小红4号
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明7号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明7号 play with 小红5号
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明0号 我的心在等待,永远在等待...
小明0号 play with 小红6号
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明5号 我的心在等待,永远在等待...
小明5号 play with 小红7号
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明9号 我的心在等待,永远在等待...
小明9号 play with 小红8号
小明2号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明2号 我的心在等待,永远在等待...
小明2号 play with 小红9号
最后,聪明的读者就自己对比一下,两份输出有什么不一样的地方,就可以搞定wait()
和wait(long)
的区别,这时也应该能明白为什么wait
方法的调用要写在while
循环里面了.
打赏
如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)
c++11 Condition Variable notify_one notify_all wait
最近看了下c++11的线程。看了不少博客,这里会引用到CSDN里Nine-days博客里的demo。
notify_one:唤醒等待线程中的一个。
notify_all:唤醒所有等待的线程。
wait:等待。需要其它的接口来唤醒。
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
//demo
std::mutex mtx_syn;
std::condition_variable cv_syn;
std::condition_variable cv_syn_1;
bool ready = false;
//线程A
void threadA(int id)
{
while (1)
{
std::unique_lock<std::mutex> lck(mtx_syn);
while (!ready)
{
cv_syn.wait(lck);
}
// ...
std::cout << "thread " << id << ''\n'';
Sleep(500);
cv_syn.notify_one(); // 唤醒等待线程中的一个
cv_syn.wait(lck); // 等待
}
}
// 线程B
void threadB(int id)
{
while (1)
{
//新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞
std::unique_lock<std::mutex> lck(mtx_syn);
while (!ready)
{
cv_syn.wait(lck);
}
// ...
std::cout << "thread " << id << ''\n'';
Sleep(500);
cv_syn.notify_one(); // 唤醒等待线程中的一个
cv_syn.wait(lck);
}
}
// 线程C
void threadC(int id)
{
while (1)
{
std::unique_lock<std::mutex> lck(mtx_syn);
while (!ready) cv_syn.wait(lck);
// ...
std::cout << "thread " << id << ''\n'';
Sleep(500);
cv_syn.notify_one(); // 唤醒等待线程中的一个线程
cv_syn.wait(lck);
}
}
void go()
{
std::unique_lock<std::mutex> lck(mtx_syn);
ready = true;
cv_syn.notify_one(); // 唤醒等待线程中的一个线程
}
int main()
{
//线程同步
std::thread threads[5];
threads[0] = std::thread(threadA, 0);
threads[1] = std::thread(threadB, 1);
threads[2] = std::thread(threadC, 2);
std::cout << "3 threads ready to race...\n";
go(); // go!
for (auto& th : threads) th.join();
}
3 threads ready to race...
thread 0
thread 1
thread 2
thread 0
thread 1
thread 2
thread 0
thread 1
thread 2
thread 0
thread 1
thread 2
通过main函数,知道实例化了3个线程。线程A,B,C在创建后被阻塞。go函数实现对线程的唤醒。
执行后发现go函数通过cv_syn.notify_one();唤醒的线程是A。
A线程,执行cv_syn.notify_one();唤醒的线程是B。
B线程,执行cv_syn.notify_one();唤醒的线程是C。
C线程,执行cv_syn.notify_one();唤醒的线程是A。
这里有一个没弄懂的地方。通过cv_syn.notify_one()唤醒,是依次唤醒,而不是随机。
#include <iostream>
#include <thread>
#include <condition_variable>
#include <mutex>
//demo
std::mutex mtx_syn;
std::condition_variable cv_syn;
std::condition_variable cv_syn_1;
bool ready = false;
//线程A
void threadA(int id)
{
while (1)
{
std::unique_lock<std::mutex> lck(mtx_syn);
while (!ready)
{
cv_syn.wait(lck);
}
// ...
std::cout << "thread " << id << ''\n'';
Sleep(500);
cv_syn.notify_all(); // 唤醒所有等待线程:B和C,B和C将会竞争锁
cv_syn.wait(lck);
}
}
// 线程B
void threadB(int id)
{
while (1)
{
//新创建的 unique_lock 对象管理 Mutex 对象 m,并尝试调用 m.lock() 对 Mutex 对象进行上锁,如果此时另外某个 unique_lock 对象已经管理了该 Mutex 对象 m,则当前线程将会被阻塞
std::unique_lock<std::mutex> lck(mtx_syn);
while (!ready)
{
cv_syn.wait(lck);
}
// ...
std::cout << "thread " << id << ''\n'';
Sleep(500);
cv_syn.notify_all(); // 唤醒所有等待线程:A和C,它们将会竞争锁
cv_syn.wait(lck);
}
}
// 线程C
void threadC(int id)
{
while (1)
{
std::unique_lock<std::mutex> lck(mtx_syn);
while (!ready) cv_syn.wait(lck);
// ...
std::cout << "thread " << id << ''\n'';
Sleep(500);
cv_syn.notify_all(); // 唤醒所有等待线程:A和B,它们将会竞争锁
cv_syn.wait(lck);
}
}
void go()
{
std::unique_lock<std::mutex> lck(mtx_syn);
ready = true;
cv_syn.notify_one(); // 唤醒等待线程中的一个线程
}
int main()
{
//线程同步
std::thread threads[5];
threads[0] = std::thread(threadA, 0);
threads[1] = std::thread(threadB, 1);
threads[2] = std::thread(threadC, 2);
std::cout << "3 threads ready to race...\n";
go(); // go!
for (auto& th : threads) th.join();
}
3 threads ready to race...
thread 0
thread 1
thread 2
thread 0
thread 1
thread 2
thread 0
thread 2
thread 0
thread 2
thread 0
thread 1
thread 0
通过输出知道,cv_syn.notify_all()唤醒了等待的所有线程,但是这些被唤醒的线程需要去竞争锁,只有获取到锁,才可以进行输出。
而cv_syn.notify_one()则不是这样,它没有竞争。
这样go里使用notify_one就不合适。应该改为notify_all. 三个线程就会对锁进行竞争。
输出如下
3 threads ready to race...
thread 2
thread 1
thread 2
thread 1
thread 2
thread 1
thread 2
thread 0
thread 2
thread 0
thread 2
thread 0
thread 2
thread 0
java condition await signal signalall对比wait notify notifyall
http://www.cnblogs.com/dolphin0520/p/3920385.html
Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作。比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权。因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。这种互相通信的过程就是线程间的协作。
今天我们就来探讨一下Java中线程协作的最常见的两种方式:利用Object.wait()、Object.notify()和使用Condition
以下是本文目录大纲:
一.wait()、notify()和notifyAll()
二.Condition
三.生产者-消费者模型的实现
若有不正之处请多多谅解,并欢迎批评指正。
请尊重作者劳动成果,转载请标明原文链接:
http://www.cnblogs.com/dolphin0520/p/3920385.html
一.wait()、notify()和notifyAll()
wait()、notify()和notifyAll()是Object类中的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/**
* Wakes up a single thread that is waiting on this object''s
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object''s
* monitor by calling one of the wait methods
*/
public
final
native
void
notify();
/**
* Wakes up all threads that are waiting on this object''s monitor. A
* thread waits on an object''s monitor by calling one of the
* wait methods.
*/
public
final
native
void
notifyAll();
/**
* Causes the current thread to wait until either another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object, or a
* specified amount of time has elapsed.
* <p>
* The current thread must own this object''s monitor.
*/
public
final
native
void
wait(
long
timeout)
throws
InterruptedException;
|
从这三个方法的文字描述可以知道以下几点信息:
1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。
2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;
4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;
有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。
上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);
notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。
同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。
nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。
这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。
举个简单的例子:假如有三个线程Thread1、Thread2和Thread3都在等待对象objectA的monitor,此时Thread4拥有对象objectA的monitor,当在Thread4中调用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一个能被唤醒。注意,被唤醒不等于立刻就获取了objectA的monitor。假若在Thread4中调用objectA.notifyAll()方法,则Thread1、Thread2和Thread3三个线程都会被唤醒,至于哪个线程接下来能够获取到objectA的monitor就具体依赖于操作系统的调度了。
上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。
下面看一个例子就明白了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
public
class
Test {
public
static
Object object =
new
Object();
public
static
void
main(String[] args) {
Thread1 thread1 =
new
Thread1();
Thread2 thread2 =
new
Thread2();
thread1.start();
try
{
Thread.sleep(
200
);
}
catch
(InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
static
class
Thread1
extends
Thread{
@Override
public
void
run() {
synchronized
(object) {
try
{
object.wait();
}
catch
(InterruptedException e) {
}
System.out.println(
"线程"
+Thread.currentThread().getName()+
"获取到了锁"
);
}
}
}
static
class
Thread2
extends
Thread{
@Override
public
void
run() {
synchronized
(object) {
object.notify();
System.out.println(
"线程"
+Thread.currentThread().getName()+
"调用了object.notify()"
);
}
System.out.println(
"线程"
+Thread.currentThread().getName()+
"释放了锁"
);
}
}
}
|
无论运行多少次,运行结果必定是:

线程Thread-1调用了object.notify()
线程Thread-1释放了锁
线程Thread-0获取到了锁
二.Condition
Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,在阻塞队列那一篇博文中就讲述到了,阻塞队列实际上是使用了Condition来模拟线程间协作。
- Condition是个接口,基本的方法就是await()和signal()方法;
- Condition依赖于Lock接口,生成一个Condition的基本代码是lock.newCondition()
- 调用Condition的await()和signal()方法,都必须在lock保护之内,就是说必须在lock.lock()和lock.unlock之间才可以使用
Conditon中的await()对应Object的wait();
Condition中的signal()对应Object的notify();
Condition中的signalAll()对应Object的notifyAll()。
三.生产者-消费者模型的实现
1.使用Object的wait()和notify()实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
public
class
Test {
private
int
queueSize =
10
;
private
PriorityQueue<Integer> queue =
new
PriorityQueue<Integer>(queueSize);
public
static
void
main(String[] args) {
Test test =
new
Test();
Producer producer = test.
new
Producer();
Consumer consumer = test.
new
Consumer();
producer.start();
consumer.start();
}
class
Consumer
extends
Thread{
@Override
public
void
run() {
consume();
}
private
void
consume() {
while
(
true
){
synchronized
(queue) {
while
(queue.size() ==
0
){
try
{
System.out.println(
"队列空,等待数据"
);
queue.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.poll();
//每次移走队首元素
queue.notify();
System.out.println(
"从队列取走一个元素,队列剩余"
+queue.size()+
"个元素"
);
}
}
}
}
class
Producer
extends
Thread{
@Override
public
void
run() {
produce();
}
private
void
produce() {
while
(
true
){
synchronized
(queue) {
while
(queue.size() == queueSize){
try
{
System.out.println(
"队列满,等待有空余空间"
);
queue.wait();
}
catch
(InterruptedException e) {
e.printStackTrace();
queue.notify();
}
}
queue.offer(
1
);
//每次插入一个元素
queue.notify();
System.out.println(
"向队列取中插入一个元素,队列剩余空间:"
+(queueSize-queue.size()));
}
}
}
}
}
|
2.使用Condition实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
public
class
Test {
private
int
queueSize =
10
;
private
PriorityQueue<Integer> queue =
new
PriorityQueue<Integer>(queueSize);
private
Lock lock =
new
ReentrantLock();
private
Condition notFull = lock.newCondition();
private
Condition notEmpty = lock.newCondition();
public
static
void
main(String[] args) {
Test test =
new
Test();
Producer producer = test.
new
Producer();
Consumer consumer = test.
new
Consumer();
producer.start();
consumer.start();
}
class
Consumer
extends
Thread{
@Override
public
void
run() {
consume();
}
private
void
consume() {
while
(
true
){
lock.lock();
try
{
while
(queue.size() ==
0
){
try
{
System.out.println(
"队列空,等待数据"
);
notEmpty.await();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
queue.poll();
//每次移走队首元素
notFull.signal();
System.out.println(
"从队列取走一个元素,队列剩余"
+queue.size()+
"个元素"
);
}
finally
{
lock.unlock();
}
}
}
}
class
Producer
extends
Thread{
@Override
public
void
run() {
produce();
}
private
void
produce() {
while
(
true
){
lock.lock();
try
{
while
(queue.size() == queueSize){
try
{
System.out.println(
"队列满,等待有空余空间"
);
notFull.await();
}
catch
(InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(
1
);
//每次插入一个元素
notEmpty.signal();
System.out.println(
"向队列取中插入一个元素,队列剩余空间:"
+(queueSize-queue.size()));
}
finally
{
lock.unlock();
}
}
}
}
}
|
- 摘要:通过之前讨论的锁对象,我们知道了,由于线程按照时间片调度,所以使用锁对象来在多线程共享资源时保护未执行完成的线程安全。那么,我们再来考虑这样一种情况:如果我的线程执行过程中因为没有满足一些必要的条件而导致线程暂停执行怎么办?比如,我们还用银行账户系统做例子,如果有一条线程是从我的账户转出1000元到其他账户,可是我的账户余额不足1000元,那么怎么办?也许你会直接简单地想到,加上一个if条件语句做一下判断不就可以了,就像这样:if(bank.getBalance(from)&
通过之前讨论的锁对象,我们知道了,由于线程按照时间片调度,所以使用锁对象来在多线程共享资源时保护未执行完成的线程安全。那么,我们再来考虑这样一种情况:
如果我的线程执行过程中因为没有满足一些必要的条件而导致线程暂停执行怎么办?
比如,我们还用银行账户系统做例子,如果有一条线程是从我的账户转出 1000 元到其他账户,可是我的账户余额不足 1000 元,那么怎么办?也许你会直接简单地想到,加上一个 if 条件语句做一下判断不就可以了,就像这样:
-
if (bank.getBalance(from) >= amount)
-
bank.transfer(from, to, amount);
但是,要注意,千万不能这样写,因为,很有可能会出现这样的情况:
1. 先执行 if 语句检查我的账户余额,余额满足条件
2. 线程时间片结束被中断暂停
3. 在这期间执行了一条从我的账户取钱的线程,取出钱后余额就不足了
4. 线程恢复执行,此时余额不足但是已经执行完毕了 if 语句
由此可见,这样的代码藏有致命的 bug ,那么,我再来做修改,也许我们可以把锁对象用上,这样即使线程暂停也不会受影响了。是的,这样做确实可以防止其他线程对余额的操作,可是,这里面还是有问题:
比如,我的余额一开始就不够,这时恰好也有一个存钱的线程进来,如果钱能顺利存进来我的余额就足够了,可是,我们的锁对象却把存钱线程拒之门外,这样反而不利于线程的顺利执行了
鉴于此,我们就需要引入条件对象
通过调用 newCondition 方法可以获得一个条件对象,而且,应该养成一个给每个条件对象起个好名字的习惯,应该用其所表达的条件为其命名,这样使人一目了然。在文中的例子中,我们用 sufficientFunds(余额充足)作为条件对象的名字
-
class Bank{
-
private Condition sufficientFunds;
-
...
-
public Bank(){
-
...
-
sufficientFunds = bankLock.newCondition();
-
}
-
}
如果 transfer 方法发现余额不足的时候,就会调用:
sufficientFunds.await( );
这时,当前线程就被阻塞(Blocked)了,并且放弃了锁对象,等待着其他的线程满足它所需的条件
等待获得锁的线程和调用 await 的线程存在本质上的不同,一旦一个线程调用 await 方法,它进入该条件的等待集,当锁可用时,该线程不能马上解除阻塞,相反,它处于阻塞状态,直到另一个线程调用同一条件上的 signalAll 方法为止
在本例中,当我们的条件对象调用 await 方法处于阻塞状态时,它就在等待一个转账存钱的线程来满足它的条件,因此,我们在写代码时,就可以为转账存钱的线程最后调用 sufficientFunds.signalAll( ) 方法
这一调用重新激活因为这一条件而等待的所有线程,当这些线程从等待集中移出时,它们再次成为可运行的,调度器将再次激活它们。同时,它们将试图重新进入该对象。一旦锁成为可用的,它们中的某个将从 await 调用返回,获得该锁并从被阻塞的地方继续执行。
因此,当条件对象被重新激活从 await 返回时,应该再次测试条件,因为即使我的账户已经有了收入,条件还不一定被满足
我们应该将 await 调用放入循环体中
-
while (条件没有被满足)
-
condition.await();
我们还应该注意的是,当一个线程调用 await 后,它无法激活自身,只能依靠等待其他的线程来满足它的条件才能继续执行,如果没有其他线程满足它的条件,它将永远无法继续执行,这就是 死锁 现象
那么,应该在什么时候调用 signalAll 方法呢?应该在每次对象状态有利于等待线程的方向改变时调用。也就是本例中,一个账户余额发生改变时调用
综上所述,最终的 transfer 方法应该写成这样:
-
public void transfer(int from, int to, int amount){
-
bankLock.lock();
-
try{
-
while(accounts[from] < amount)
-
sufficientFunds.await();
-
// transfer funds
-
...
-
sufficientFunds.signalAll();
-
}
-
finally{
-
bankLock.unlock();
-
}
-
}
最后还要注意,Java 中有 signal 和 signalAll 两种方法,signal 是随机解除一个等待集中的线程的阻塞状态,signalAll 是解除所有等待集中的线程的阻塞状态。signal 方法的效率会比 signalAll 高,但是它存在危险,因为它一次只解除一个线程的阻塞状态,因此,如果等待集中有多个线程都满足了条件,也只能唤醒一个,其他的线程可能会导致死锁
-
java notify,wait,notifyAll理解和实例(一)
通常可以使用synchronized和notify,notifyAll以及wait方法来实现线程之间的数据传递及控制。对于对象obj来说:
obj.wait():该方法的调用,使得调用该方法的执行线程(T1)放弃obj的对象锁并阻塞,直到别的线程调用了obj的notifyAll方法、或者别的线程调用了obj的notify方法且JVM选择唤醒(T1),被唤醒的线程(T1)依旧阻塞在wait方法中,与其它的线程一起争夺obj的对象锁,直到它再次获得了obj的对象锁之后,才能从wait方法中返回。(除了notify方法,wait还有带有时间参数的版本,在等待了超过所设时间之后,T1线程一样会被唤醒,进入到争夺obj对象锁的行列;另外中断可以直接跳出wait方法)
obj.notify():该方法的调用,会从所有正在等待obj对象锁的线程中,唤醒其中的一个(选择算法依赖于不同实现),被唤醒的线程此时加入到了obj对象锁的争夺之中,然而该notify方法的执行线程此时并未释放obj的对象锁,而是离开synchronized代码块时释放。因此在notify方法之后,synchronized代码块结束之前,所有其他被唤醒的,等待obj对象锁的线程依旧被阻塞。
obj.notifyAll():与notify的区别是,该方法会唤醒所有正在等待obj对象锁的线程。(不过同一时刻,也只有一个线程可以拥有obj的对象锁)
要注意的是,wai,notify以及notifyAl
我们今天的关于Lua coroutine vs Java wait/notify的分享就到这里,谢谢您的阅读,如果想了解更多关于(三)java多线程之wait notify notifyAll、c++11 Condition Variable notify_one notify_all wait、java condition await signal signalall对比wait notify notifyall、java notify,wait,notifyAll理解和实例(一)的相关信息,可以在本站进行搜索。
本文标签: