Java 中”i++”是线程安全得吗? 一次面试题引发的学习
什么是原子操作?
所谓原子操作, 可以简单理解为一次不可分割的操作, 同时可以保证线程安全. 例如:
int i = 1;
如上代码表示原子操作, 即一次性赋值, 没有多余的中间操作.
那么 i++ 是否是原子操作呢?
同样的方式来分析 i++ 操作. 可以分为三步:
- 获取上一次 i 值.
- 将 i 的值 +1.
- 赋值给 i.
由上可见 i++ 并非不可分割操作, 所以 i++ 并不是原子操作, 即 i++ 并非线程安全.
那么在 java 中如何保证自增操作的线程安全呢?
方法有 3 种:
- 利用同步–Synchronized.
- 利用 Java 提供给我们的 AtomicInteger、AtomicLong…
- 利用 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;} }
上述代码我们定义了四个类型的变量:
- 静态 int.
- AtomicInteger Java 提供原子操作.
- Counter 同步方式.
- volatile 可见性关键字 (这里不对该关键字做讨论).
我们来看一下运行结果:
可见,Java 中可以保证自增为线程安全的方法是 Atomic 以及 Synchronized.
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!