Java中的原子性和可见性

2017-11-08  本文已影响302人  e小e

原子性:在java语言原子性是指的一个不可以分割的操作,比如说 a = 0,这个就具有原子性,如果是a++, 这个操作其实是 a = a+1; 是可以分割的,所以他就不是一个原子性操作。
非原子操作具有线程安全问题,我们需要使用相关的手段,保证线程同步,java.util.concurrent.atomic包下面提供了一系列的原子操作类,如AtomicInteger、AtomicLong、AtomicReference等

可见性:是指线程之间的可见性,当一个线程修改一个变量,另外一个线程可以马上得到这个修改值。比如:用volatile修饰的变量是具有可见性的。volatile修饰的变量不允许线程内部缓存和
重新排序,即直接修改内存,所以对其它线程是可见的。但是这里需要注意一个问题volatile修饰的变量只是具有可见性,而不具备原子性。比如 volatile int a = 0; 之后操作一个 a++,那么a只是具有可见性,而
不具备原子性,a++同样具有线程安全的问题。

他们之间关系
原子性是一个操作是否一步完成,可见性是指其它线程是否可见。它们其实并没有任何关系。

public class Test {  
  
    volatile int a = 1;  
    volatile boolean ready;  
      
    public class PrintA extends Thread{  
        @Override  
        public void run() {  
            while(!ready){  
                Thread.yield();  
            }  
            System.out.println(a);  
        }  
    }  
    public static void main(String[] args) throws InterruptedException {  
        Test t = new Test();  
        t.new PrintA().start();  
        //下面两行如果不加volatile的话,执行的先后顺序是不可预测的。并且下面两行都是原子操作,但是这两行作为一个整体的话就不是一个原子操作。  
        t.a = 48; //这是一个原子操作,但是其结果不一定具有可见性。加上volatile后就具备了可见性。  
        t.ready = true;//同理  
    }  
  
}  

上面程序如果变量a不用volatile修饰那么输出结果很可能就是0.。

AtomicInteger、AtomicLong、AtomicBoolean,AtomicReference前三个都很好理解是对Integer,Long,Boolean做原子性操作的类,那这个AtomicReference是什么呢?
它主要是对引用对象进行原子性操作:举个栗子。

class Person {  
    volatile long id;  
    public Person(long id) {  
        this.id = id;  
    }  
    public String toString() {  
        return "id:"+id;  
    }
public static void main(String[] args){  
  
        // 创建两个Person对象,它们的id分别是101和102。  
        Person p1 = new Person(101);  
        Person p2 = new Person(102);  
        // 新建AtomicReference对象,初始化它的值为p1对象  
        AtomicReference ar = new AtomicReference(p1);  
        // 通过CAS设置ar。如果ar的值为p1的话,则将其设置为p2。  
        ar.compareAndSet(p1, p2);  
  
        Person p3 = (Person)ar.get();  
        System.out.println("p3 is "+p3);  
        System.out.println("p3.equals(p1)="+p3.equals(p1));  
    }  

运行结果:
p3 is id:102
p3.equals(p1)=false

结果说明:
新建AtomicReference对象ar时,将它初始化为p1。
紧接着,通过CAS函数对它进行设置。如果ar的值为p1的话,则将其设置为p2。
最后,获取ar对应的对象,并打印结果。p3.equals(p1)的结果为false,这是因为Person并没有覆盖equals()方法,而是采用继承自Object.java的equals()方法;而Object.java中的equals()实际上是调用"=="去比较两个对象,即比较两个对象的地址是否相等。

上一篇下一篇

猜你喜欢

热点阅读