目标:

  1. 掌握并发编程中变量可见性问题
  2. 掌握造成线程安全、变量可见性问题的原因
  3. 掌握volatile关键字的用途、使用场景

如何在多个线程中共享数据,让变量可见?

  • 使用final修饰变量(不可变变量)
  • 使用synchronized同步代码块
import java.util.concurrent.TimeUnit;

/**
 * @ClassName VisibilityDemo
 * @Description
 * @Author 夕
 * @Date 2016-07-27 23:07
 * @Version V1.0
 **/
public class VisibilityDemo {
    //状态标识
    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("i===============>"+i);
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(2);
        }catch (Exception e){
            e.printStackTrace();
        }
        VisibilityDemo.is = false;
        System.out.println("VisibilityDemo.is,被设置为false了");
    }
}

运行结果:

VisibilityDemo.is,被设置为false了
i===============>84562795
  • 使用volatile关键字修饰变量
import java.util.concurrent.TimeUnit;

/**
 * @ClassName VisibilityDemo
 * @Description
 * @Author 夕
 * @Date 2016-07-27 23:07
 * @Version V1.0
 **/
public class VisibilityDemo {
    //状态标识
    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("i===============>"+i);
            }
        }).start();

        try {
            TimeUnit.SECONDS.sleep(2);
        }catch (Exception e){
            e.printStackTrace();
        }
        VisibilityDemo.is = false;
        System.out.println("VisibilityDemo.is,被设置为false了");
    }
}

运行结果:

VisibilityDemo.is,被设置为false了
i===============>1329322071

造成线程安全、变量可见性问题的原因是java内存模型原理造成的

小知识补充:

java内存模型及操作规范
* 共享变量必须存在放在内存中
* 线程有自己的工作内存,现在只能操作自己的工作内存
* 线程要操作共享变量,需从主内存中读取到共享变量,改变值后需从工作内存中同步到主内存中

请思考问题,有变量A,多线程并发对其累加会有什么问题?如三个线程并发操作变量A,大家读取A
时都读取到A=0,都对A+1,再将值同步会主内存。结果是多少?

答案是1,原因就是各自线程都操作自己的工作内存,都读取到的是A=0,加1,然后再同步回主内存
,自然就是1了。

synchronized语义规范:

  1. 进入同步块前,先清空工作内存中的共享变量,从主内存中重新加载
  2. 解锁前必须把修改的共享变量同步回主内存中

注意:synchronized用的必须是同一个对象

synchronized是如何做到线程安全的

  1. 锁机制保护共享资源,只有获得锁的才可以操作共享资源
  2. synchronized语义规范保证了修改共享资源后,会同步回主内存,就做到了线程安全。

volatile关键字的用途、使用场景

语义规范:

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

volatile是做不到线程安全的,因为volatile没有锁机制。

为什么要使用volatile关键字

  1. volatile比synchronized简单
  2. volatile性能更好

volatile另外一个用途:可用于限制局部代码指令重排序