java - 为什么 wait(100) 导致同步方法在多线程中失败?
问题描述
我从 Baeldung.com 引用。不幸的是,这篇文章没有解释为什么这不是线程安全的代码。文章
我的目标是了解如何使用 synchronized 关键字创建线程安全方法。
我的实际结果是:计数值为 1。
package NotSoThreadSafe;
public class CounterNotSoThreadSafe {
private int count = 0;
public int getCount() { return count; }
// synchronized specifies that the method can only be accessed by 1 thread at a time.
public synchronized void increment() throws InterruptedException { int temp = count; wait(100); count = temp + 1; }
}
我的预期结果是:计数值应该是 10,因为:
- 我在一个池中创建了 10 个线程。
- 我执行了
Counter.increment()
10 次。 - 我确保仅在 CountDownLatch 达到 0 后进行测试。
- 因此,它应该是 10。但是,如果您释放
lock
synchronized usingObject.wait(100)
,该方法将变得不是线程安全的。
package NotSoThreadSafe;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CounterNotSoThreadSafeTest {
@Test
void incrementConcurrency() throws InterruptedException {
int numberOfThreads = 10;
ExecutorService service = Executors.newFixedThreadPool(numberOfThreads);
CountDownLatch latch = new CountDownLatch(numberOfThreads);
CounterNotSoThreadSafe counter = new CounterNotSoThreadSafe();
for (int i = 0; i < numberOfThreads; i++) {
service.execute(() -> {
try { counter.increment(); } catch (InterruptedException e) { e.printStackTrace(); }
latch.countDown();
});
}
latch.await();
assertEquals(numberOfThreads, counter.getCount());
}
}
解决方案
为什么int temp = count; wait(100); count = temp + 1;
不是线程安全的?一种可能的流程:
- 第一个线程读取
count
(0),将其保存以temp
备后用,然后等待,允许第二个线程运行(释放锁); - 第二个线程读取
count
(也是 0),保存在 中temp
,然后等待,最终允许第一个线程继续; - 第一个线程从(1)
temp
中增加值并保存;count
count
但是第二个线程仍然保存(0)的旧值temp
- 最终它将运行并将temp+1
(1) 存储到count
中,而不是增加它的新值。
非常简化,只考虑 2 个线程
简而言之:wait()
释放允许其他(同步)方法运行的锁。
推荐阅读
- jquery - 将命名对象添加到数组
- python - 比较python中单列pandas数据框的值(将perl转换为python代码)
- asp.net-mvc - 内部加入多个列表
- r - 如何在大型 data.table (57M obs) 中快速搜索?
- javascript - JavaScript 访问无效索引
- python - 如何使用python pynput和opencv检测linux中键盘上的按键组合?
- node.js - Firebase 服务器错误:“无法加载默认凭据”
- r - 在 R 中寻找下面行中的值
- powershell - 查找 PowerShell 全局变量定义的位置
- c - 试图让猜数字程序在 3 次猜测后结束