Java精通并发-多线程同步关系实例剖析与详解

在上一次https://www.cnblogs.com/webor2006/p/11422587.html中通过实践来解了一个案例,先来回顾一下习题:

编写一个多线程程序,实现这样的一个目标:

1、存在一个对象,该对象有一个int类型的成员变量counter,该成员变量的初始值为0。

2、创建两个线程,其中一个线程对该对象的成员变量counter增1,另一个线程对该对象的成员变量减1。

3、输出该对象成员变量counter每次变化后的值。

4、最终输出的结果应为:101010101010。。。。

具体实现的代码就不回顾了,在上次的文末提到了这么一句:

下面来验证一下这个“完美”,目前这个程序只有一个线程去增,另一个线程去减,那。。如果对于增和减对应多个线程又会如何呢?下面改造一下:

 

看下结果是否依然如预期:

很明显一开始输出就不对了,另外!!

那。。为啥呢?两个线程就妥妥的,升级一下线程数就出问题,其实在以前https://www.cnblogs.com/webor2006/p/8419565.html的这篇博文中也专门学习过,这里再回忆捋一下:

现有是有两个增加线程和两个减少线程,假如目前有一个增加线程执行了增加方法,此时的counter++=1,如下:

接着用来减少的线程执行减少操作了,由于counter目前是1,所以可以正常执行到减操作,所以counter--=0:

好,第三个线程还是减少的线程又来执行减少操作了,而由于此时的counter=0,所以这个减少线程会进行wait(),如下:

接着还剩的一个减少线程又来执行减少操作了,此时由于counter还是等于0,那。。由于wait()是会将当前对象的锁给释放的,所有减小的两个线程此时都处于wait()状态了。

好接下来增加的一个线程来了,执行增加操作,由于减少的两个线程都处于wait()状态都不会持有对象的锁了,所以这个增加线程肯定是能正常执行增加操作的,目前count就会由0变为1了,如下:

接着注意重点来了:

好,正在wait()的两个减少的线程其中一个被唤醒了,所以接下来该线程就会将count--=0,如下:

好,重点又来了:

那么,最后一个wait()的减小线程又执行了一次counter--=-1:

所以结果就已经出问题了,其中需要特别注意的是:

也就是说:

回归到这个程序的问题上来,其实本质是由于:

所以,咱们来改一下程序:

好,咱们再运行一下:

一切正常了,而且可以看到程序也正常可以退出来。这也如官方文档所说:

其中还有一个原因需要注意:“spurious wakeups”,假唤醒,如果不放循环中肯定也会出问题。

原文地址:https://www.cnblogs.com/webor2006/p/11423023.html