首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Java9和更高版本中线程间通信不能正常工作。

在Java9和更高版本中线程间通信不能正常工作。
EN

Stack Overflow用户
提问于 2019-03-08 20:32:59
回答 1查看 214关注 0票数 0

在线程调度器的幕后,有些东西从Java8变成了Java9。我正试图缩小以下程序的变化范围。

下面的程序生成3个线程,它们并行地运行,并同步地通过监视器锁,打印。

代码语言:javascript
复制
Aa0Bb1Cc2Dd3.......Zz25

当前代码在所有Java版本中都运行良好,我不需要任何优化。

在使用Object.notifyAll()传递锁之前,我使用了Object.wait() (这可能并不总是正确的,但在这种情况下,它在java1.8中并没有起到作用)。这就是为什么有两个版本的这个代码版本1和版本2。

第1版在所有java版本(Java8和优先版本、Java9及更高版本)中运行良好。但不是第2版。例如,当您评论版本1和不注释版本2时,如下所示

代码语言:javascript
复制
//obj.wait();//version 1
obj.notifyAll();obj.wait();//version 2

它在Java8中的运行与在Java9和以后的JDK中完全一样。它无法抓住锁,或者抓住了锁,但是条件已经翻转,没有线程了。

(例如,假设麻木线程完成了它的工作,现在只有能够捕获锁并继续执行的线程才是ThreadCapital,但不知怎么地,isCapital变成了假的--这只是一个推测,不能证明这一点,或者说不能确定这是否发生了。)

我很少有使用线程的经验,所以我确信我没有利用监视器上的锁,即使我有它,也应该在所有JDK中反映同样的情况。除非在Java9和更高版本中发生了一些变化。线程调度程序内部有什么变化吗?

代码语言:javascript
复制
    package Multithreading_misc;

    public class App {

        public static void main(String[] args) throws InterruptedException {

            SimpleObject obj = new SimpleObject();
            ThreadAlphaCapital alpha = new ThreadAlphaCapital(obj);
            ThreadAlphaSmall small   = new ThreadAlphaSmall(obj);
            ThreadNum num            = new ThreadNum(obj);

            Thread tAlpha = new Thread(alpha);
            Thread tSmall = new Thread(small);
            Thread tNum   = new Thread(num);

            tAlpha.start();
            tSmall.start();
            tNum.start();

        }
    }

    class ThreadAlphaCapital implements Runnable{
        char c = 'A';
        SimpleObject obj;

        public ThreadAlphaCapital(SimpleObject obj){
            this.obj = obj;
        }

        @Override
        public void run() {
            try {
                synchronized (obj) {
                    while(c < 'Z')      
                        {
                            if(!obj.isCapitalsTurn || obj.isNumsTurn)
                            {   
                                obj.wait();//version 1
                                //obj.notifyAll();obj.wait();//version 2
                            }
                            else 
                            {
                                Thread.sleep(500);
                                System.out.print(c++);
                                obj.isCapitalsTurn = !obj.isCapitalsTurn;
                                obj.notifyAll();//version 1
                                //obj.notifyAll();obj.wait();//version 2
                            }   
                        }   
                    obj.notifyAll();
                }

            }
             catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
        }

    }
    class ThreadAlphaSmall implements Runnable{
        char c = 'a';
        SimpleObject obj;

        public ThreadAlphaSmall(SimpleObject obj){
            this.obj = obj;
        }

        @Override
        public void run() {
            try {
                synchronized (obj) {
                    while(c < 'z')      
                        {           
                            if(obj.isCapitalsTurn || obj.isNumsTurn)
                            {
                                obj.wait();//version 1
                                //obj.notifyAll();obj.wait();//version 2
                            }
                            else 
                            {
                                    Thread.sleep(500);
                                    System.out.print(c++);
                                    obj.isCapitalsTurn = !obj.isCapitalsTurn;
                                    obj.isNumsTurn = !obj.isNumsTurn;
                                    obj.notifyAll();//version 1
                                    //obj.notifyAll();obj.wait();//version 2    
                            }   
                        }   
                    obj.notifyAll();
                }
            }
            catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    }

    class ThreadNum implements Runnable{

        int i = 0;
        SimpleObject obj;

        public ThreadNum(SimpleObject obj){
            this.obj = obj;
        }
        @Override
        public void run() {
            try {   
                synchronized (obj) {
                    while(i < 26)       
                        {
                            if(!obj.isNumsTurn)
                            {   
                                obj.wait();//version 1
                                //obj.notifyAll();obj.wait();//version 2
                            }
                            else 
                            {
                                Thread.sleep(500);
                                System.out.print(i++);
                                obj.isNumsTurn = !obj.isNumsTurn;
                                obj.notifyAll();//version 1
                                //obj.notifyAll();obj.wait();//version 2
                            }   
                        }
                    obj.notifyAll();    
                }
            }
            catch (InterruptedException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }
    }

    class SimpleObject{
        public boolean isNumsTurn = false;
        public boolean isCapitalsTurn = true;   
    }

注意事项少:

  1. 当从UnNamed和更高版本运行时,这是从Java9模块运行的。
  2. 我并不是说这只发生在3个线程上,只是举个例子。顺便说一下,对于所有java版本的两个线程,它(过度通知)都可以正常运行。
EN

回答 1

Stack Overflow用户

发布于 2019-03-11 15:31:26

我相信过度通知。

目前还不清楚为什么要相信这一点,也不清楚在代码中洒下notifyAll()会有什么好处,但现在是时候成为怀疑论者了。

这可能不是一直都是正确的,但在这种情况下,并没有什么不同。

很明显,这确实有区别。

是的,JVM的等待队列实现的某些方面似乎已经改变了,但这并不重要,因为您的代码总是被中断,并且完全靠运气运行。

这种情况其实很容易理解:

  1. 线程A更改状态,使线程B有资格继续并调用notifyAll()
  2. 线程B和C由于notifyAll()而唤醒,并尝试重新获取锁。哪一个会赢,还没有说明。
  3. 线程C获取锁,发现自己不合格并再次转到wait(),但在第二个变体中,它将首先执行伪notifyAll()
  4. 线程A和B由于虚假的notifyAll()而醒来(B可能已经醒了,但这并不重要)并尝试重新获取锁。哪一个会赢,还没有说明。
  5. 线程A获得锁,发现自己不合格并再次转到wait(),但在第二个变体中,它将首先执行伪notifyAll()
  6. 线程B和C由于虚假的notifyAll()而醒来(B可能已经醒了,但这并不重要),并尝试重新获取锁。哪一个会赢,还没有说明。
  7. 见3。

正如您所看到的,使用第二个变体,您有一个可能永远运行的潜在循环,只要B永远得不到锁。使用过时的notifyAll()调用的变体依赖于错误的假设,即如果通知多个线程,正确的线程最终将收到锁。

在适合使用notifyAll()的地方使用notify()没有问题,因为所有性能良好的线程都会重新检查它们的条件,如果不满足,将再次转到wait(),因此正确的线程(或一个合格的线程)最终将取得进展。但是在等待之前调用notifyAll()并不是很好的行为,可能会导致线程永久地重新检查它们的条件,而不会让合格的线程得到它的回报。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55070602

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档