volatile的理解

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;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,137评论 11 349
  • 不记得是谁说过,每天都要做两件让自己痛苦的事情,才不会辜负这美好时光。 坚持,是件痛苦的事情;但我认为痛苦才会是美...
    BobBruno阅读 729评论 1 51
  • 今天最后一天,也是最难受的一天。今天的遵旨是不吃不喝一整天,除了早上的300毫升温开水。一早起来烧开水,喝了一杯...
    G高静阅读 458评论 0 0
  • 你心目中理想的爱情是什么样的?或许每个人都有不同的答案,而我也在追寻着自己的答案。在这个过程中,有一首歌,也可以说...
    Lily_after20阅读 785评论 0 0