volitile的使用

volatileche称为轻量级锁,被volatile修饰的变量,在线程之间是可见的。
可见:一个线程修改了这个变量的值,在另一个线程中能够读到这个修改后的值
synchronized除了线程之间互斥,还有一个非常大的作用,就是保证可见性

 1 /*
 2  * 保证可见性的前提
 3  * 
 4  * 多个线程拿到同一把锁,否则保证不了
 5  * 
 6  */
 7 public class Demo {
 8     private int a = 1;
 9 
10     public synchronized int getA() {
11         return a;
12     }
13 
14     public synchronized void setA(int a) {
15         try {
16             Thread.sleep(10);
17         } catch (InterruptedException e) {
18             // TODO Auto-generated catch block
19             e.printStackTrace();
20         }
21         this.a = a;
22     }
23     
24     public static void main(String[] args) {
25         Demo demo = new Demo();
26         
27         new Thread(new Runnable() {
28 
29             @Override
30             public void run() {
31                 demo.setA(10);    //保证这个线程先执行
32             }
33         }).start();
34         
35         
36         new Thread(new Runnable() {
37 
38             @Override
39             public void run() {
40              System.out.println(demo.getA());
41             }
42         }).start();
43         
44         try {
45             Thread.sleep(100);
46         } catch (InterruptedException e) {
47             // TODO Auto-generated catch block
48             e.printStackTrace();
49         }
50          System.out.println("最终结果为:"+demo.getA());
51     }
52     
53 }

10
最终结果为:10

volatile的底层是怎样实现的?
我们发现,在汇编的环境下,加了volatile来修饰的变量和没加volatile的区别是。加了volatile的比没volatile的多了一个lock指令。因此lock指令起来作用。因此让我们来看看什么是lock指令。
 
什么是lock指令?
在多处理器的系统上,(注意,目的是为了实现可见性!)
1、将当前处理器缓存行的内容协会到系统内容(缓存行是缓存的最小单位)
2、这个写回到内存的操作会使在其他CPU里缓存该内存地址的数据失效
public class Demo2 {
    
    public volatile boolean run = false;
    
    public static void main(String[] args) {
        
        Demo2 d = new Demo2();
        new Thread(new Runnable() {

            @Override
            public void run() {
                for(int i = 1; i<=10; i++) {
                    System.out.println("执行了第"+i+"次");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }    
                    d.run = true;
            }
        }).start();
        
        
        
        
        new Thread(new Runnable() {

            @Override
            public void run() {
                while(!d.run) {
                    //不执行
                }
                System.out.println("线程2执行了");
            }
        }).start();
    }
}

//执行了第1次
//执行了第2次
//执行了第3次
//执行了第4次
//执行了第5次
//执行了第6次
//执行了第7次
//执行了第8次
//执行了第9次
//执行了第10次
//线程2执行了

java虚拟机的类加载,就是把硬盘的或网络上的等等的字节码文件加载到内存中(CPU的速度>缓存速度>内存速度>硬盘速度),当我们用了volatile来修饰变量的时候,虚拟机会把该变量缓存到我们CPU缓存中,而且会把变量写回到内存中(没有加volatile的话,则不会把变量写回到内存中)而写回到内存的这个操作,会使在其他CPU里缓存该内存地址的数据失效,缓存上没数据,只能在内存中读回来,这样就保证了变量的可见性,如果我们在同一个类中大量地使用volatile的话,那么cpu的缓存机制就失效了,那么就性能就降低了

synchronized与volatile区别
volatile不能保证变量的非原子性操作,只能保证可见性
synchronized 可以保持变量的非原子性操作(如i++),也能保证可见性。
就是说,能用上volatile的话,能用synchronized替换。用上了synchronized,不一定能用volatile替换

 

posted @ 2019-02-13 07:03  曲阳阳  阅读(561)  评论(0编辑  收藏  举报