首页 > 解决方案 > 为什么 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,因为:

  1. 我在一个池中创建了 10 个线程。
  2. 我执行了Counter.increment()10 次。
  3. 我确保仅在 CountDownLatch 达到 0 后进行测试。
  4. 因此,它应该是 10。但是,如果您释放locksynchronized using Object.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());
  }
}

标签: javamultithreading

解决方案


为什么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()释放允许其他(同步)方法运行的锁。


推荐阅读