变量可见性问题

简介

并发编程时各个线程中无法获取到共享变量的最新值。


产生的原因

共享变量存储在主内存中,通常情况下各个线程在使用某个共享变量时先将共享变量复制进线程工作内存中,后续使用到该变量时直接从当前线程工作内存中获取变量值,此时如果其他线程更改了该共享变量值那么当前线程无法实时更新到该变量的最新值。
同步交互协议中规定的8种原子操作

  • lock:将主内存中的变量锁定,为某一个线程独占
  • unlock:将lock加的锁解除,此时该变量可以被其他线程访问
  • read:作用于主内存变量,将主内存中的变量读到工作内存当中
  • load:作用于工作内存变量,将read读取到的值保存到工作内存的变量副本中
  • use:作用于工作内存变量,将值传递给线程代码执行引擎
  • assign:作用于工作内存变量,将执行引擎返回的值重新赋值给变量副本中
  • store:作用于工作内存变量,将变量副本的值传递给主内存
  • write:作用于主内存变量,将store传过来的值写入到主内存共享变量中

示例

如下代码while循环会一直执行知道cpu达到100%应用程序崩溃
p

rivate static boolean is=true;
public static void main(String[] args){
    new Thread(new Runnable() {
        @Override
        public void run() {
            int i=0;
            while(VisibilityDemo.is){
                i++;
            }
            System.out.println("循环终止");
            System.out.println(i);
        }
    }).start();
    try{
        TimeUnit.SECONDS.sleep(2);
    }catch (InterruptedException ex){
        ex.printStackTrace();
    }
    VisibilityDemo.is=false;
    System.out.println("被置为false了");
}

解决方式

final

final修饰的变量不可变,所以不存在变量可见性问题


synchronized

synchronized语义规范

  1. 进入同步块钱,先清空工作内存中的共享变量,从主内存中重新加载
  2. 解锁前,必须把共享变量同步回主内存
    示例
private static boolean is=true;
    public static void main(String[] args){
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i=0;

                while(VisibilityDemo.is){
                    synchronized (this){
                        i++;
                    }
                }
                System.out.println("循环终止");
                System.out.println("i="+i);
            }
        }).start();
        try{
            TimeUnit.SECONDS.sleep(2);
        }catch (InterruptedException ex){
            ex.printStackTrace();
        }
        VisibilityDemo.is=false;
        System.out.println("被置为false了");
    }

运行结果

被置为false了
循环终止
i=64274144

volatile

volatile语义规范

  1. 使用volatile变量时,必须重新从主内存加载,并且read、load是连续的
  2. 修改volatile变量后,必须立马同步回主内存,并且store、write是连续的

示例

private static volatile boolean is=true;
public static void main(String[] args){
    new Thread(new Runnable() {
        @Override
        public void run() {
            int i=0;
            while(VisibilityDemo.is){
                i++;
            }
            System.out.println("循环终止");
            System.out.println(i);
        }
    }).start();
    try{
        TimeUnit.SECONDS.sleep(2);
    }catch (InterruptedException ex){
        ex.printStackTrace();
    }
    VisibilityDemo.is=false;
    System.out.println("被置为false了");
}

执行结果

被置为false了
循环终止
-860124361

synchronized和volatile区别

synchronized使用锁机制保护共享资源,只有获得锁的线程才可操作共享资源,同时synchronized语义规范保证了修改共享资源后会同步回主内存,从而做到线程安全。volatile因为没有锁机制,所以线程还是可以并发操作变量,所以没有做到线程安全
volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞
volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

发布了22 篇原创文章 · 获赞 27 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/u014395955/article/details/103598228
今日推荐