volatile 可见_java 可见性简单总结

Java 可见性

内存模型

主存

所有线程都可以访问

本地内存

每个线程私有的内存

- java 的所有变量都存储在主内存中

- 每个线程有自己独的工作内存,保存了该线程使用到的变量副本,是对主内存中变量的一份拷贝

- 每个线程不能访问其他线程的工作内存,线程间变量传递需要通过主内存来完成

- 每个线程不能直接操作主存,只能把主存的内容拷贝到本地内存后再做操作(这是线程不安全的本质),然后写回主存

可见性的方法

volatile

这种方式可以保证每次取数直接从主存取

它只能保证内存的可见性,无法保证原子性

它不需要加锁,比 synchronized 更轻量级,不会阻塞线程

验证测试

public class testRun  implements Runnable{

    public  volatile Boolean flag=false;
    public   ArrayList<String> arr =new ArrayList<>();
    @Override
    public void run() {
        while (true){
            if(arr.size()>0){
                break;
            }
        }
    }
}

       public static void main(String[] args) throws InterruptedException {
            testRun testRun = new testRun();
            new Thread(testRun).start();
            Thread.sleep(1000);
            testRun.flag=true;
            testRun.arr.add("123");


        }


测试 发现arr 不加volatile 主线程的修改不能使 测试线程正确停止

ArrayList 是线程不安全的 如果我们使用线程安全类呢

public class testRun  implements Runnable{

    public  volatile Boolean flag=false;
    public   ArrayList<String> arr =new ArrayList<>();
    public Vector<String> vec =new Vector<>();
    @Override
    public void run() {
        while (true){
            if(vec.size()>0){
                break;
            }
        }
    }
}

 public static void main(String[] args) throws InterruptedException {
            testRun testRun = new testRun();
            new Thread(testRun).start();
            Thread.sleep(1000);
            testRun.flag=true;
            testRun.vec.add("123");


        }

测试发现是可以正确停止

public HashMap<String,Object> has =new HashMap<>();

public Hashtable<String,Object> hashtable =new Hashtable<String,Object>();

public ConcurrentHashMap<String,Object> concurrentHashMap =new ConcurrentHashMap<String,Object>();

测试发现 HashMap 需要加volatile  关键字才可以正确停止 线程安全的hashtable与concurrentHashMap则不需要 具体为啥 不清楚

关于 volatile 的影响范围

        不管事基本数据类型像 int 还是引用数据类型 像 String 或者自己创建的 实体类Objext 在被共享访问时都存在可见性问题

两个有趣的demo

        

public class TestDemo3 {
     public   int i=1;

}

   

public class Test1 {
    static       volatile TestDemo3 t3=new TestDemo3();
    static       TestDemo3 t4=t3;
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    if(t4.i!=1){
                    break;
                    }
                }

            }
        },"main1").start();
        Thread.sleep(500);
        new Thread(new Runnable() {
            @Override

            public void run() {
                t3.i=2;
            }
        },"main2").start();

    }
}

这种情况为什么不能解决可见性问题 

      原因   volatile  的可见性是通过  读屏障来实现的   获取 t4.i 的值  因为 t4 变量没有加volatile 导致 没有 加读屏障所以获取的值 可见性

这种情况为什么又能保证可见性

public class TestDemo3 {
     public   int i=1;

}

   

public class Test1 {
    static       volatile TestDemo3 t3=new TestDemo3();
    static       TestDemo3 t4=t3;
    public static void main(String[] args) throws InterruptedException {

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    if(t3.i!=1){
                    break;
                    }
                }

            }
        },"main1").start();
        Thread.sleep(500);
        new Thread(new Runnable() {
            @Override

            public void run() {
                t4.i=2;
            }
        },"main2").start();

    }
}

分析 

主存的数据同步是根据写屏障 实现的  理论上   t4.i=2;赋值因为 t4没有加 volatile关键字 那么

不会同步到主存中 但实际 这个demo仍然能保证可见性不知道为什么

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值