原子变量
为了引出原子变量这个概念,我们先看一个例子。
- 1 package com.ccfdod.juc;
- 2 3 public class TestAtomicDemo {
- 4 5 public static void main(String[] args) {
- 6 AtomicDemo ad = new AtomicDemo();
- 7 8
- for (int i = 0; i < 10; i++) {
- 9 new Thread(ad).start();
- 10
- }
- 11
- }
- 12
- }
- 13 14 class AtomicDemo implements Runnable {
- 15 private int number = 0;
- 16 17@Override 18 public void run() {
- 19
- try {
- 20 Thread.sleep(200);
- 21
- } catch(InterruptedException e) {
- 22 e.printStackTrace();
- 23
- }
- 24 System.out.println(Thread.currentThread().getName() + " : " + getNumber());
- 25
- }
- 26 27 public int getNumber() {
- 28
- return number++;
- 29
- }
- 30
- }
程序运行结果如下:
- 1 Thread-4 : 2
- 2 Thread-0 : 6
- 3 Thread-2 : 3
- 4 Thread-5 : 5
- 5 Thread-7 : 4
- 6 Thread-3 : 1
- 7 Thread-6 : 2
- 8 Thread-1 : 0
- 9 Thread-9 : 8
- 10 Thread-8 : 7
从程序运行结果可以看出,Thread-4 和 Thread-6 执行结果都为 2,明显发生了线程安全问题,当然,这种情况是偶然的。那么,出现这种问题的原因是什么呢?
如果你对 j = i++; 底层是如果实现的,那么这个问题就好理解了。j = i++; 底层实现为:
- int temp = i;
- i = i + 1;
- j = temp;
那么很明显,Thread-4(或 Thread-6)在执行改操作加 1 之前,Thread-6(或 Thead-4)读到了相同的值。然后都进行加 1 操作,再打印出来。
对于这类问题,我们可以使用原子变量来解决。在 jdk1.5 后,java.util.current.atomic 包中提供了常用的原子变量。原子变量有一下特性:
CAS 算法
CAS 算法是硬件对于并发操作共享数据的支持,CAS 包含了三个操作数:
并且,当且仅当 V==A 时,V=B,否则,将不做任何操作。
在了解了原子变量后,我们使用原子变量修改程序:
- 1 class AtomicDemo implements Runnable {
- 2 // private int number = 0;
- 3 private AtomicInteger number = new AtomicInteger();
- 4 5@Override 6 public void run() {
- 7
- try {
- 8 Thread.sleep(200);
- 9
- } catch(InterruptedException e) {
- 10 e.printStackTrace();
- 11
- }
- 12 System.out.println(Thread.currentThread().getName() + " : " + getNumber());
- 13
- }
- 14 15 public int getNumber() {
- 16 // return number++;
- 17
- return number.getAndIncrement();
- 18
- }
- 19
- }
来源: