美文网首页
volatile的理解

volatile的理解

作者: senninha | 来源:发表于2017-06-14 20:32 被阅读35次
    volatile的理解

    首先上代码

    public class VolatileTest implements Runnable{
        private boolean isStop;
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            VolatileTest v = new VolatileTest();
            Thread t = new Thread(v);
            t.start();
            try{
                Thread.sleep(1000);
            }catch(Exception e){
            }
            v.isStop = true;
        }
        
        public void run() {
            while(!isStop){
    
            }
            System.out.println("停止");
        }
    
    }
    

    运行这段代码,把jvm设置为server模式,主线程在启动子线程后,休眠1s,子线程在这个时候进入一个条件循环,1s后主线程把循环条件置为true,但是这个时候会发现子线程进入了死循环,这是因为无法看到主线程对isStop变量的修改.
    普遍的解释说法是线程内部维护了一个isStop的变量副本,子线程为了提高运行效率,从变量副本里取isStop,所以主线程对isStop的修改对于子线程不可见.
    那么问题来了,这个变量副本是存在哪里的呢?


    rocomp博客中看到

    线程对共享变量的所有操作都必须在自己的工作内存(working memory,是cache和寄存器的一个抽象,而并不是内存中的某个部分,这个解释源于《Concurrent Programming in Java: Design Principles and Patterns, Second Edition》§2.2.7,原文:Every thread is defined to have a working memory (an abstraction of caches and registers) in which to store values. 有不少人觉得working memory是内存的某个部分,这可能是有些译作将working memory译为工作内存的缘故,为避免混淆,这里称其为工作存储,每个线程都有自己的工作存储)中进行,不能直接从相互内存中读写


    所以说以上的那个程序,子线程一直从自己的工作内存里取出isStop的变量副本,一直无法发现共享内存里已经改变了值.
    普遍的解决方法是,对isStop声明了volatile变量,这样,对变量的读写都会从共享内存里去取,而不是从工作内存(缓存)里拿.

    volatile的语义就是在该变量被修改的时候,不使用线程的缓存,(这里的缓存指的可能是寄存器或者是cpu的1,2,3级高速缓存).直接从共享内存(ram)里取出值.

    那么除了将isStop声明为volatile变量,还有其他方法能避免线程使用缓存吗?
    误打误撞,发现如果在死循环中打印或者进行wait的话,这个死循环会退出.就是说在进行吃i/0输出或者等待的时候导致了缓存失效?

    稍微改改代码:

    public class VolatileTest implements Runnable{
        private boolean isStop;
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            VolatileTest v = new VolatileTest();
            for(int i = 0 ; i < 1 ; i++){
                Thread t = new Thread(v,"线程" + i);
                t.start();
            }
            try{
                Thread.sleep(1000);
            }catch(Exception e){
            }
            v.isStop = true;
        }
        
        public void run() {
            while(!isStop){
                try {
                //休眠足够时间,好去看看对应进程里线程在干啥
                    Thread.currentThread().sleep(30000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("停止");
        }
    
    }
    

    打印出的线程状态:

    "线程0" #9 prio=5 os_prio=0 tid=0x00007f818c0dd800 nid=0x1968 waiting on condition [0x00007f816dedb000]
       java.lang.Thread.State: TIMED_WAITING (sleeping)
            at java.lang.Thread.sleep(Native Method)
            at cn.senninha.swordtooffer.thread.VolatileTest.run(VolatileTest.java:22)
            at java.lang.Thread.run(Thread.java:745)
    

    似乎可以得出结论,一旦线程进入等待,缓存就会失效,然后就会从共享内存(ram)里去取值.

    扯远了,所以volatile的特性就是:

    1.可见性:对于volatile变量的读,总是看到任意线程对这个volatile变量最后的写入:

    就是说,a线程如果对一个volatile变量进行了写入,那么其他线程对于该volatile变量的缓存就会失效,转而从共享内存里去获取这个变量最新的值.

    2.原子性:对单个volatile变量的读写具有原子性

    另外,只要对一个变量用volatile声明,不管它是多复杂的一个类,他的所有变量经过修改后都会马上写回共享内存里,保证可见性,比如如下示例:

    /**
    *声明VolatileNode为volatile变量,实际上用来通信表示线程需要停止的是VolatileNode对象里
    *的VolatileNode1里的isStop变量,但是,在这种情况下,isStop的改变仍然会马上写回共享内存
    *里,保证可见性.所以,在声明volatile变量时候,粒度应该尽量小一点,不然应该会影响到效率.
    **/
    public class VolatileTest implements Runnable{
        private boolean isStop;
        private volatile VolatileNode node = new VolatileNode();
        public static void main(String[] args) throws InterruptedException {
            // TODO Auto-generated method stub
            VolatileTest v = new VolatileTest();
            for(int i = 0 ; i < 1 ; i++){
                Thread t = new Thread(v,"线程" + i);
                t.start();
            }
            Thread.currentThread().sleep(1000);
            v.node.node1.isStop = true;
        }
        
        public void run() {
            while(!node.node1.isStop){
                
            }
            System.out.println("停止");
        }
    
    }
    
    class VolatileNode{
        public boolean isStop;
        public VolatileNode1 node1 = new VolatileNode1();
    }
    
    class VolatileNode1{
        public boolean isStop;
    }
    

    相关文章

      网友评论

          本文标题:volatile的理解

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