johnbanq 在排除了IMSE错误之后,我们接着解决这个问题——
在修好了异常并且去掉了.get()调用(它是阻塞的,会等待这个任务(thread1)整个函数跑完,对于这个双线程协同任务而言是不可接受的)后,我们运行程序,发现程序会随机地卡在某个count值上,使用debugger进行检查,发现两个线程检查线程状态,发现两个线程都进入了WAIT状态,并且都卡在了各自的await调用里。
这看起来像是条件变量通知时刻和其它线程等待时刻的错位问题,那我们就加点println:
        Callable<String> thread1= new Callable<String>() {
            @Override
            public String call() throws Exception {
                while(count<100) {
                    if(count==100){
                        break;
                    }
                    int cnt = count;
                    if (cnt % 2 == 0) {
                        //先判断再获取锁
                        lock.lock();
                        count++;
                        //Thread.sleep(100);
                        System.out.println("loop:"+cnt+" thread 1 is waking everyone up");
                        condition.signalAll();
                        lock.unlock();
                    }else{
                        lock.lock();
                        System.out.println("loop:"+cnt+" thread 1 decided to suspend");
                        condition.await();
                        System.out.println("loop:"+cnt+" thread 1 is awake");
                        lock.unlock();
                    }
                }
                return "线程1执行完毕";
            }
        };
        Callable<String> thread2= new Callable<String>() {
            @Override
            public String call() throws Exception {
                while(count<100){
                    if(count==100){
                        break;
                    }
                    int cnt = count;
                    if(cnt%2==1){
                        //Thread.sleep(100);
                        lock.lock();
                        count++;
                        System.out.println("loop:"+cnt+" thread 2 is waking everyone up");
                        condition.signalAll();
                        lock.unlock();
                    } else {
                        lock.lock();
                        System.out.println("loop:"+cnt+" thread 2 decided to suspend");
                        System.out.flush();
                        condition.await();
                        System.out.println("loop:"+cnt+" thread 2 is awake");
                        lock.unlock();
                    }
                    //lock.unlock();
                }
                return "线程2结束";
            }
        };
追问:为什么要单独看一个覆盖范围这么大的cnt变量来保存count的值?
出于原子性考虑,从判断到打印中间可能发生任何事,拷进变量里能避开这个坑
执行得到如下日志:
loop:0 thread 1 is waking everyone up
loop:1 thread 1 decided to suspend
loop:0 thread 2 decided to suspend
然后就卡住不动了。
如果我们仔细看看打出来的东西,会发现结尾2个线程都跑去suspend了,没有人在干活。
再动动脑筋,我们就可以还原出真相:
最开始是循环0:
 线程2先发制人,执行到了int cnt=count, 然后出于某些原因(例如太窜了),被OS从CPU上面挪了下来,把1换上去了。
 线程1起来把活干了,通知了一下正在等的人可以开始干活了,就去suspend了。
 线程2被OS重新挪上了CPU,开始等待1的通知。
需要注意的是Condition 的通知 不是持久的,如果你来晚了,就不会收到那个通知。于是可怜的2在等待队列里一直suspend,等那个早就发了的通知。