Java 中”i++”是线程安全得吗? 一次面试题引发的学习

什么是原子操作?

  • 所谓原子操作, 可以简单理解为一次不可分割的操作, 同时可以保证线程安全. 例如:

      int i = 1;
  • 如上代码表示原子操作, 即一次性赋值, 没有多余的中间操作.

那么 i++ 是否是原子操作呢?

  • 同样的方式来分析 i++ 操作. 可以分为三步:

    1. 获取上一次 i 值.
    2. 将 i 的值 +1.
    3. 赋值给 i.
  • 由上可见 i++ 并非不可分割操作, 所以 i++ 并不是原子操作, 即 i++ 并非线程安全.

那么在 java 中如何保证自增操作的线程安全呢?

  • 方法有 3 种:

    1. 利用同步–Synchronized.
    2. 利用 Java 提供给我们的 AtomicInteger、AtomicLong…
    3. 利用 Lock.
  • 我们来看几种不同的方式获得的结果.

      public class IncrementTest {
          public static int count = 0;
          public static Counter counter = new Counter();
          public static AtomicInteger atomicInteger = new AtomicInteger(0);
          public volatile static int countVolatile = 0;
          public static Lock lock = new ReentrantLock();
          public static int countLock = 0;
    
          public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {for (int j = 0; j < 1000; j++) {
                              count++;
                              counter.increment();
                              atomicInteger.getAndIncrement();
                              countVolatile++;
                              lockIncrement();}
                  }).start();}
    
              try {Thread.sleep(3000);
              } catch (InterruptedException e) {e.printStackTrace();
              }
    
              System.out.println("static int -> " + count);
              System.out.println("counter -> " + counter.getValue());
              System.out.println("atomicInteger -> " + atomicInteger.get());
              System.out.println("volatile int -> " + countVolatile);
              System.out.println("count lock -> " + countLock);
          }
          public static void lockIncrement() {lock.lock();
              countLock++;
              lock.unlock();}
      }
    
      class Counter {
          private int value;
    
          public synchronized int getValue() {return value;}
    
          public synchronized int increment() {return ++value;}
    
          public synchronized int decrement() {return --value;}
      }
  • 上述代码我们定义了四个类型的变量:

    1. 静态 int.
    2. AtomicInteger Java 提供原子操作.
    3. Counter 同步方式.
    4. volatile 可见性关键字 (这里不对该关键字做讨论).
  • 我们来看一下运行结果:

    increment

  • 可见,Java 中可以保证自增为线程安全的方法是 Atomic 以及 Synchronized.



Java     

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!