使用同步块的Java中的并发性未给出预期结果

前端之家收集整理的这篇文章主要介绍了使用同步块的Java中的并发性未给出预期结果前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

下面是一个简单的java程序.它有一个名为“cnt”的计数器,它会递增,然后添加到名为“monitor”的List中. “cnt”由多个线程递增,并且值被多个线程添加到“监视器”.

方法“go()”的末尾,cnt和monitor.size()应该具有相同的值,但它们不具有相同的值. monitor.size()确实有正确的值.

如果通过取消注释其中一个已注释的同步块来更改代码,并注释掉当前未注释的块,则代码生成预期结果.此外,如果将线程计数(THREAD_COUNT)设置为1,则代码生成预期结果.

这只能在具有多个真实核心的计算机上重现.

public class ThreadTester {

    private List
最佳答案
好的,让我们来看看你提到的不同可能性:

1.

for (int ii=0; ii

首先,监视器对象在线程之间共享,因此对它进行锁定(这就是synchronized所做的)将确保块内的代码一次只能由一个线程执行.因此,外部的2内部同步不是必需的,无论如何代码都受到保护.

2.

for (int ii=0; ii

好的,这个有点棘手. cnt是一个Integer对象,Java不允许修改Integer对象(整数是不可变的),即使代码表明这是在这里发生的事情.但是,实际上会发生的是cnt将使用值cnt 1创建一个新的Integer并覆盖cnt.
这就是代码实际执行的操作:

synchronized(cnt) {
  Integer tmp = new Integer(cnt + 1);
  cnt = tmp;
}

问题是,一个线程将创建一个新的cnt对象,而所有其他线程都在等待锁定旧的cnt对象.线程现在释放旧的cnt,然后尝试获取新cnt对象的锁定并获取它,而另一个线程获取旧cnt对象的锁定.突然,2个线程处于临界区,执行相同的代码并导致竞争条件.这是错误结果的来源.

如果删除第一个同步块(带监视器的块),那么结果会更加错误,因为竞争的可能性会增加.

通常,您应该尝试仅对最终变量使用synchronized来防止这种情况发生.

原文链接:https://www.f2er.com/java/437263.html

猜你在找的Java相关文章