示例

如果多个线程对同一个共享数据进行访问而不采取同步操作的话,那么操作的结果是不一致的。

以下代码演示了 1000 个线程同时对 cnt 执行自增操作,操作结束之后它的值有可能小于 1000。

  1. public class ThreadUnsafeExample {
  2. private int cnt = 0;
  3. public void add() {
  4. cnt++;
  5. }
  6. public int get() {
  7. return cnt;
  8. }
  9. public static void main(String[] args) throws InterruptedException {
  10. final int threadSize = 1000;
  11. ThreadUnsafeExample example = new ThreadUnsafeExample();
  12. final CountDownLatch countDownLatch = new CountDownLatch(threadSize);
  13. ThreadPoolExecutor executor = new ThreadPoolExecutor(
  14. 5, 10, 1, TimeUnit.SECONDS,
  15. new ArrayBlockingQueue<>(100), new ThreadPoolExecutor.CallerRunsPolicy());
  16. for (int i = 0; i < threadSize; i++) {
  17. executor.execute(() ->{
  18. example.add();
  19. countDownLatch.countDown();
  20. });
  21. }
  22. countDownLatch.await();
  23. executor.shutdown();
  24. System.out.println(example.get());
  25. }
  26. }
  27. // 输出结果
  28. 998 //如果线程是安全的最终结果就是 1000

解决线程安全问题

使用 AtomicInteger

(实际上非阻塞同步)

  1. public class ThreadSafeExample {
  2. private AtomicInteger cnt = new AtomicInteger(0);
  3. public void add() {
  4. cnt.incrementAndGet();
  5. }
  6. public int get() {
  7. return cnt.get();
  8. }
  9. }

使用 synchronized

(实际上是阻塞同步/互斥同步)

  1. public class ThreadSafeExample {
  2. private int cnt = 0;
  3. public synchronized void add() {
  4. cnt++;
  5. }
  6. public synchronized int get() {
  7. return cnt;
  8. }
  9. }

使用 Reentrant

(实际上是阻塞同步/互斥同步)

  1. public class ThreadSafeExample {
  2. private int cnt = 0;
  3. private ReentrantLock lock = new ReentrantLock();
  4. public void add() {
  5. lock.lock();
  6. try {
  7. cnt++;
  8. } finally {
  9. lock.unlock();
  10. }
  11. }
  12. public int get() {
  13. lock.lock();
  14. try {
  15. return cnt;
  16. } finally {
  17. lock.unlock();
  18. }
  19. }
  20. }