Java多线程打印静态变量异常输出11的原因排查及指定输出实现
异常产生原因
核心问题源自wait()方法的执行特性和代码的条件校验逻辑缺陷:
wait()会让当前线程释放锁陷入阻塞,被notify()唤醒后,线程会直接从wait()调用的位置继续向下执行,不会重新校验外层的循环边界条件i <= 10。- 代码中等待条件使用
if判断,仅会在校验进入等待前判断一次条件,唤醒后不会二次校验等待条件是否仍然成立,也不会校验i是否已经超出打印边界。
具体执行流程对应你给出的输出:
- 当
i=10时,Thread-0(负责打印1-4、8-10)进入同步块,i<=10判断成立,且i=10不属于5-7的等待区间,直接打印10 Thread-0,执行i++后i变为11,调用notify()唤醒处于阻塞状态的Thread-1。 - Thread-1此前因
i=8命中等待条件陷入阻塞,被唤醒后直接向下执行打印逻辑,此时i=11就被异常输出,之后才会回到外层while(i<=10)判断,发现条件不成立退出循环。
修复方案
按照Java多线程wait()的最佳实践做两处修改即可:
- 所有等待条件从
if判断改为while循环判断,确保线程被唤醒后会重新校验等待条件是否仍然成立,同时覆盖边界校验。 - 打印逻辑前增加边界判断,避免
i超出10后仍然执行打印。
修改后的核心代码如下:
void print2() { synchronized (this) { while (i <= 10) { // 等待条件改为while循环,唤醒后重新校验 while (i <=10 && ((i > 0 && i < 5) || (i > 7 && i < 11))) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 边界校验,超出范围直接退出 if(i > 10) break; System.out.println(i + " " + Thread.currentThread().getName()); i++; notify(); } } } void print1() { synchronized (this) { while (i <= 10) { // 等待条件改为while循环 while (i <=10 && i > 4 && i < 8) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 边界校验,超出范围直接退出 if(i > 10) break; System.out.println(i + " "+Thread.currentThread().getName()); i++; notify(); } } }
内容的提问来源于stack exchange,提问作者Akash Kumar Singh
相关产品推荐
相关产品推荐

