• Java多线程编程——volatile关键字


    (本篇主要内容摘自《Java多线程编程核心技术》)

    volatile关键字的主要作用是保证线程之间变量的可见性。

    package com.func;
    
    public class RunThread extends Thread{
        private boolean isRunning = true;
    //    volatile private boolean isRunning = true;
        
        public boolean isRunning() {
            return isRunning;
        }
        
        public void setRunning(boolean isRunning) {
            this.isRunning = isRunning;
        }
        
        @Override
        public void run(){
            System.out.println("进入 run 了!");
            while (isRunning == true) {
            }
            System.out.println("停止运行了!");
        }
        
    }
    package com.test;
    
    import com.func.RunThread;
    
    public class Test3 {
        public static void main(String[] args){
            try {
                RunThread runThread = new RunThread();
                runThread.start();
                Thread.sleep(1000);
                runThread.setRunning(false);
                System.out.println("isRunning已经赋值为false");
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        
        }
    
    }

    将JVM设置为-server时就会出现下面状况

    可以看到 System.out.println("停止运行了!"); 没有得到运行,也就是说isRunning 一直是true的状态。 runThread.setRunning(false); 这句话并没有起到相应的作用。

    那么为什么会这样呢?

    原因就在于私有堆栈和公共堆栈中的值不一致造成的。当把JVM设置成-server的方式,为了执行效率,线程会一直在私有堆栈中取值。 runThread.setRunning(false);将公共堆栈中的isRunning设置成为false,但是私有堆栈中的isRunning 并没有得到同步。

    面对这种情况就可以采用volatile关键字解决。将isRunniing变量使用volatile关键字修饰。

    volatile private boolean isRunning = true;

    volatile关键字强制线程从公共堆栈中获取变量值,而不是从私有堆栈中获取变量值。

    使用volatile关键字可以增加实力变量在多个线程之间的可见性。但是volatile关键字只是保证可见性并不保证原子性。不能使用volatile关键字来保证线程安全

    下面的例子会说明volatile关键字不能保证线程安全。

    package com.func;
    
    public class MyThread extends Thread{
        volatile public static int count;
    //    public static int count;
        
        private static void addCount(){
    //    synchronized private static void addCount(){     
            for (int i = 0; i < 100; i++) {
                count ++;
            }
            System.out.println("count = " + count);
        }
        
        @Override
        public void run(){
            addCount();
        }
    }
    package com.test;
    
    import com.func.MyThread;
    
    public class Test {
        
        public static void main(String[] args){
            try {
                MyThread[] myThreads = new MyThread[100];
                for (int i = 0; i < 100; i++) {
                    myThreads[i] = new MyThread();
                }
                for (int i = 0; i < myThreads.length; i++) {
                    myThreads[i].start();
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    
    }

    可以看见最后的结果并不是10000,看见volatile并不能保证线程安全。虽然数据都是从公共堆栈取出来的,但是数据的取出来之后的修改并不是原子操作(count++这样的操作并不是原子的),所以最后将修改后的数据进行同步时,数据并不是我们想要的。

    为了保证数据的原子性,我们还是需要使用synchronized关键字。synchronized关键字保证了原子性,解决了多个线程之间访问资源的同步性。

    synchronized private static void addCount(){ 
      ...
    }
  • 相关阅读:
    递归浅析
    python3中zip()的用法
    在早期IBP病人中比较风湿病医生诊断中轴型SpA(aSpA)与非aSpA
    超声检查附着点在早期SpA诊断中的应用
    验证MRI检测AS病人骶髂关节骨侵蚀、扩展侵蚀和回填
    EULAR2008_TNF拮抗剂保护RA骨关节的机制可能不止是抑制滑膜炎
    RA关节功能残疾与软骨破坏的相关性高于骨破坏
    TNFBA治疗强柱达8年的放射学评估
    荟萃分析随机对照临床试验显示抗TNF治疗未增加早期RA病人的严重感染和肿瘤发生风险
    早期IBP病人骶髂关节MRI炎症与1年后MRI结构破坏之间的关系
  • 原文地址:https://www.cnblogs.com/cuglkb/p/7269620.html
Copyright © 2020-2023  润新知