美文网首页
Java中的原子性和可见性

Java中的原子性和可见性

作者: e小e | 来源:发表于2017-11-08 08:28 被阅读302次

    原子性:在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()实际上是调用"=="去比较两个对象,即比较两个对象的地址是否相等。

    相关文章

      网友评论

          本文标题:Java中的原子性和可见性

          本文链接:https://www.haomeiwen.com/subject/aoqsmxtx.html