首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 .NET Java 游戏 视频 人才 外包 培训 数据库 书店 程序员
中国软件网
欢迎您:游客 | 登录 注册 帮助
  • 双核CPU上运行Notify 与wait的通知丢失现象 [已结贴,结贴人:denverbenjamin2000]
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 15:56:35 楼主
    大家好,最近写一个多线程程序,用到了wait/notify ,做了个小实验,发现在双核和单核上跑的结果有很大差异

    单核上,如java手册上写的一样,双核,则notify不到执行wait的部分,难道是wait在实现上不支持多cpu的问题(如操作系统教材中所说,有些利用中断的原语不能用于多核?)请大家指正

    代码如下:
    Java code
    package thread1; // this program is puzzle when run on 2 core ,I do not know why ,it just // do not stop class ThreadA { public static void main(String[] args) { ThreadB b = new ThreadB(); b.start(); System.out.println("b is start...."); synchronized (b)// 括号里的b是什么意思,起什么作用? { try { System.out.println("Waiting for b to complete..."); b.wait();// 这一句是什么意思,究竟让谁wait? System.out.println("Completed.Now back to main thread"); } catch (InterruptedException e) { System.out.println("InterruptedException"); } } System.out.println("main:Total is :" + b.total); } } class ThreadB extends Thread { int total; public void run() { synchronized (this) { System.out.println("ThreadB is running.."); for (int i = 0; i < 10; i++) { total += i; System.out.println("total is " + total); } notify(); /* * try { System.out.println("b is waiting" ); wait(); * } catch (InterruptedException e) { System.out.println("InterruptedException"); } */ } System.out.println("AFTER notify"); System.exit(0); } }
    该帖子于2008-05-10 08:12:34被版主修改
    100  修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 18:28:051楼 得分:1
    学习, 帮顶了
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 18:55:322楼 得分:4
        synchronized (b)// 括号里的b是什么意思,起什么作用? 
           --------------括号里的b是监听器对象,这个对象是可以随意的!但是涉及到多个synchronized时,括号里的对象要保持一致,只有保持一致了,才能够达到想要的同步.

        b.wait();// 这一句是什么意思,究竟让谁wait? 
      肯定是让b去wait啊.
      
        你这个程序我觉得错误就是根本没有了解synchronized的用法啊. 对象没有一致,也既是监听器对象没有一致.导致出现结果在双核和单核上运行的差异,其实你在单核上运行2次的结果也应该是不一样的.因为没有达到同步,结果应该就是随机的.

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 19:01:553楼 得分:4
    synchronized (b)//增加了个锁,锁定了对象b,在同一个类实例中,是线程安全的,但不同的实例还是不安全的。
    b.wait();// 让b线程wait
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 19:06:164楼 得分:0
    如果希望,主程序把b所在的thread启动起来后就等待b线程执行完,应该如何写呢
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 19:29:205楼 得分:4
    我觉得你这个程序就是正确的,反正我没看出程序本身哪里有问题
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 19:30:416楼 得分:4
    你可以在b所在的thread启动代码后面让主线程sleep下不就是了
              sleep()的时间你可以自己在()里面设置,是以毫秒为单位的  
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 19:40:077楼 得分:6
    引用 6 楼 javazhuobin 的回复:
    你可以在b所在的thread启动代码后面让主线程sleep下不就是了 
              sleep()的时间你可以自己在()里面设置,是以毫秒为单位的  


    我觉得这个程序似乎不需要用sleep哎
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-06 21:44:448楼 得分:1
    学习
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 00:00:109楼 得分:0
    引用 2 楼 javazhuobin 的回复:
    synchronized (b)// 括号里的b是什么意思,起什么作用?
    --------------括号里的b是监听器对象,这个对象是可以随意的!但是涉及到多个synchronized时,括号里的对象要保持一致,只有保持一致了,才能够达到想要的同步.

    b.wait();// 这一句是什么意思,究竟让谁wait?
    肯定是让b去wait啊.

    你这个程序我觉得错误就是根本没有了解synchronized的用法啊. 对象没有一致,也既是监听器对象没有一致.导致出现结果在双核和单核上运行的差…


    我对此表示怀疑,您能具体点说么,
    synchronized (b){
    b.wait(); 

    此处我认为是让当前线程等待b的获取,而不是b去等待
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • mdog26
    • 等级:
    发表于:2008-05-07 00:41:4710楼 得分:3
    学习了,双核是偶也遇到这个问题
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 00:56:2411楼 得分:10
    不是双核的问题,而的你的线程死锁的问题

    lz改用一下jdk自带的线程池可以解决这样的问题

    根据cpu数量乘以每个cpu的线程数来创建池

    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 02:33:0012楼 得分:0
    引用 11 楼 kokobox 的回复:
    不是双核的问题,而的你的线程死锁的问题

    lz改用一下jdk自带的线程池可以解决这样的问题

    根据cpu数量乘以每个cpu的线程数来创建池




    你好!看了您的回复,也找了java的线程池资料,
     private ExecutorService exe=null;//线程池
      exe.execute(new Worker(i));/
      
      但是我还是对于 2楼的回复感到很困惑,因为他说的颠覆了我对于wait和synchronized的理解,您能指点我一下么
      
      PS:
      引用 2 楼 javazhuobin 的回复:
    synchronized (b)// 括号里的b是什么意思,起什么作用?
    --------------括号里的b是监听器对象,这个对象是可以随意的!但是涉及到多个synchronized时,括号里的对象要保持一致,只有保持一致了,才能够达到想要的同步.

    b.wait();// 这一句是什么意思,究竟让谁wait?
    肯定是让b去wait啊.

    你这个程序我觉得错误就是根本没有了解synchronized的用法啊. 对象没有一致,也既是监听器对象没有一致.导致出现结果在双核和单核上运行的差…


    我对此表示怀疑,您能具体点说么,
    synchronized (b){
    b.wait(); 

    此处我认为是让当前线程等待b的获取,而不是b去等待
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 03:13:5113楼 得分:0
    大家好,晚上我又修改了一下这个程序,
    1 加入了一个tic布尔变量,在notify的时候设置为true,main改为一个自循环,在notify为true后才退出;
    2 将  ThreadB 中的synchronized (this) {范围虽小到了仅仅包括  tic变量设置和notify
    然后,双核也可以正常结束了,奇怪!


    Java code
    package thread1; // this program is puzzle when run on 2 core ,I do not know why ,it just // do not stop class ThreadA { public static boolean tic = false; public static void main(String[] args) { ThreadB b = new ThreadB(); b.start(); System.out.println("b is start...."); while (true) { while (ThreadA.tic == false) { synchronized (b)// 括号里的b是什么意思,起什么作用? { try { System.out.println("Waiting for b to complete..."); b.wait();// 这一句是什么意思,究竟让谁wait? System.out.println("Completed.Now back to main thread"); } catch (InterruptedException e) { System.out.println("InterruptedException"); } } } if (ThreadA.tic == true) { System.out.println("main:FINISH"); System.exit(4); } } // System.out.println("main:Total is :" + b.total); } } class ThreadB extends Thread { int total; public void run() { System.out.println("ThreadB is running.."); for (int i = 0; i < 100; i++) { total += i; System.out.println("total is " + total); } synchronized (this) { ThreadA.tic = true; notify(); /* * try { System.out.println("b is waiting" ); wait();// 这一句是什么意思,究竟让谁wait? } catch (InterruptedException e) { * System.out.println("InterruptedException"); } */ } System.out.println("AFTER notify"); System.exit(0); } }
    该回复于2008-05-10 08:13:46被版主修改
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 03:26:1214楼 得分:0
    我比较认同附录1的看法,也就是说我认为2楼对于
    "b.wait();// 这一句是什么意思,究竟让谁wait?
    肯定是让b去wait啊. "
    的解释是错的,这个地方的确很容易混淆,大家注意:
    附录1

    下:
    在线程1中执行如下代码
    ...
    synchronized(obj1) //1.进入同步块
    {
    try {
    ...
    obj1.wait(); //2.进入暂停状态
    }catch (InterruptedException exp) {}
    }

    1. 当前同步对象(monitor)为obj1,obj1是任一实例,若是同步方法,则同步对象是this.进入同步块后,obj1为锁定状态,锁定状态对 obj1本身无任何影响,而其它线程执行到同一代码时,因不能获得锁,处于Block状态,一旦解锁,被block的线程自动继续执行.
    2.调用obj1.wait()有两个作用,一是使线程进入等待状态,二是解锁obj1,这时被block的线程将获得锁.线程1要退出等待必须要由其它线程显式的调用obj1.notify()或notifyAll()方法.
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 11:22:4315楼 得分:40
    把原始程序改成如下形式,单CPU机器上运行也不能正常退出了。
    Java code
    public class ThreadTest { public static void main(String[] args) { ThreadB b = new ThreadB(); b.start(); System.out.println("b is start...."); try { Thread.sleep(2000);//没有这个的话,单CPU上运行,成功的可能性很大 } catch (InterruptedException ex) { ex.printStackTrace(); } synchronized (b)// 括号里的b是什么意思,起什么作用? { try { Thread.sleep(2000); System.out.println("Waiting for b to complete..."); b.wait();// 这一句是什么意思,究竟让谁wait? System.out.println("Completed.Now back to main thread"); } catch (InterruptedException e) { System.out.println("InterruptedException"); } } System.out.println("main:Total is :" + b.total); } } class ThreadB extends Thread { int total; public void run() { synchronized (this) { System.out.println("ThreadB is running.."); for (int i = 0; i < 100; i++) { total += i; System.out.println("total is " + total); } notify(); /* * try { System.out.println("b is waiting" ); wait(); * } catch (InterruptedException e) { * System.out.println("InterruptedException"); } */ } System.out.println("AFTER notify"); } }

    解释:线程的启动需要时间,因此,线程b启动时,主线程很可能已经执行了synchronized (b)
    这样的话,线程run方法中的synchronized (this)就必须等待b的对象锁解锁。当主线程
    运行b.wait()后,b对象锁解开,run方法执行,同时,主线程等待b的对象锁解锁。当run方法
    运行完了后,调用notify(),b对象锁解开,主线程继续执行,程序正常退出。
    但是在这个过程中,如果线程b的run比主线程先运行到synchronized块的话,run方法先运行完了后,
    主线程才能进入synchronized块,然后就会运行到wait处等待b的对象锁解锁,但这时,b线程已经运行
    结束了,没有任何线程会把b解锁,因此程序不会退出。
    双核的机器上,这种情况出现的概率更大。
    不知道对不对,仅供参考。望高手指正。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 17:01:1016楼 得分:0
    楼上说的有一点很对,就是wait等不到notify
    这一特性源自于java锁(监视器)与从前我们写操作系统时候的锁定最大区别,那就是,前者的锁(监视器)是没有状态的,后者是有的
    Java code
    package thread1; //this program is puzzle when run on 2 core ,I do not know why ,it just // do not stop class ThreadA { public static boolean tic = false; public static void main(String[] args) { ThreadB b = new ThreadB(); b.start(); int i=1; System.out.println("b is start...."); while (true) { while (ThreadA.tic == false) { synchronized (b)// 括号里的b是什么意思,起什么作用? { try { System.out.println("Waiting for b to complete..."); b.wait();// 这一句是什么意思,究竟让谁wait? System.out.println("Completed.Now back to main thread"); } catch (InterruptedException e) { System.out.println("InterruptedException"); } } } System.out.println("loop in while true for"+i+"times"); if (ThreadA.tic == true) { System.out.println("main:FINISH"); System.exit(4); } } // System.out.println("main:Total is :" + b.total); } } class ThreadB extends Thread { int total; public void run() { System.out.println("ThreadB is running.."); for (int i = 0; i < 100; i++) { total += i; System.out.println("total is " + total); } synchronized (this) { ThreadA.tic = true; notify(); /* * try { System.out.println("b is waiting" ); wait();// * 这一句是什么意思,究竟让谁wait? * } catch (InterruptedException e) { * System.out.println("InterruptedException"); } */ } System.out.println("AFTER notify"); System.exit(0); } }
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 17:07:0817楼 得分:23
    首先,object是用来作为线程的锁的。
    调用object.wait()时,当前线程挂起,直到另外一个线程调用object.notify()---当然,如果多个线程wait这个object,就不一定是哪个被唤醒了。
    同样,object.notify()唤醒等待该object的一个线程。注意,调用notify的线程在释放锁之前,等待着的线程仍然是不能唤醒的。

    这两个方法都必须在synchronized块中进行操作,即当前线程必须拥有该object的锁。

    你遇到的问题,如果java的文档中明确写明,双核可能不被支持,那就很可能不被支持了。毕竟wait和notify最终还是要调用系统原句的,你所依赖的工具有问题,就没办法了。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 17:54:0418楼 得分:0
    引用 17 楼 fantasydog 的回复:
    首先,object是用来作为线程的锁的。
    调用object.wait()时,当前线程挂起,直到另外一个线程调用object.notify()---当然,如果多个线程wait这个object,就不一定是哪个被唤醒了。
    同样,object.notify()唤醒等待该object的一个线程。注意,调用notify的线程在释放锁之前,等待着的线程仍然是不能唤醒的。


    同意你的说法,我修改了代码,让threadB尽可能的快于主进程执行,的确出现了不唤醒现象

    Java code
    package thread1; //this program is puzzle when run on 2 core ,I do not know why ,it just // do not stop class ThreadA { public static boolean tic = false; public static void main(String[] args) { ThreadBB b = new ThreadBB(); b.start(); int i=1; System.out.println("b is start...."); //while (true) { //while (ThreadA.tic == false) { synchronized (b) { try { System.out.println("father:Waiting for b to complete..."); b.wait(); System.out.println("father:Completed.Now in main thread"); } catch (InterruptedException e) { System.out.println("InterruptedException"); } } } System.out.println("father:loop in while true for"+i+"times"); /* if (ThreadA.tic == true) { System.exit(4); } */ } // System.out.println("main:Total is :" + b.total); System.out.println("father:main:FINISH"); } } class ThreadBB extends Thread { int total; public void run() { System.out.println("ThreadB is running.."); for (int i = 0; i < 10; i++) { total += i; System.out.println("total is " + total); } synchronized (this) { System.out.println("goingto notify"); //ThreadA.tic = true; notify(); /* * try { System.out.println("b is waiting" ); wait();// * 这一句是什么意思,究竟让谁wait? * } catch (InterruptedException e) { * System.out.println("InterruptedException"); } */ } System.out.println("AFTER notify"); System.exit(0); } }
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • ZW_LM
    • 等级:
    发表于:2008-05-07 20:31:4019楼 得分:0
    开了眼界。。。支持楼主
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-07 23:59:4220楼 得分:0
    不知道你这个程序是做什么用的。
    像这样,两个线程执行顺序未知,且若A后执行,程序就会永久等待下去,是挺危险的。
    如果一定需要类似的程序,建议再建立一个守护线程,对所有被调用wait()的对象周期性的调用notify()。
    这样,就能保证不会有哪个线程在持续等待状态了。
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • bjsun
    • 等级:
    发表于:2008-05-08 09:06:2421楼 得分:0
    该回复于2008-05-08 21:20:43被版主删除
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-09 23:22:1722楼 得分:0
    学习了 顶一下
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    发表于:2008-05-09 23:34:5923楼 得分:0
    mark
    修改 删除 举报 引用 回复
    进入用户个人空间
    加为好友
    发送私信
    在线聊天
    • java2000_net
    • 等级:
    发表于:2008-05-10 08:11:2424楼 得分:0
    我的看法

        b.start();
        System.out.println("b is start....");
        synchronized (b)

    此处启动了b ,后面有拿b的同步锁,那么到底b的run里面的 同步和这个代码的同步哪个先运行到呢?

    如果 b 里面的先运行到,那么只有等b运行完毕,此处的代码才会执行,
    如果此处先运行到,则会拿到锁,但是那个 b.wait()并不是让 b 去wait而是当前线程 wait, 这个可以去看看wait的API文档。
    此时 此处的锁会被释放,运行 b里面的run方法,

    2
    我们简单修改一下代码,可以很清楚的看到
    第一种方法
        ThreadB b = new ThreadB();
            System.out.println("b is start....&quo