java多线程补:充原子性和可见性

参考:http://www.cnblogs.com/mengyan/archive/2012/08/22/2651575.html

原子性:所谓原子性就是不可分割的,比如:在我们编程中直接给变量赋值,这就是不可分割的,就具有原子性,相对的,非原子性就是在编程中步骤被分割的,比如编程中的计算,是分步骤进行的,例如:a+=b,其实编程是分为三步,1、先取出a和b的值 2、计算a+b 3、写入内存。这就是非原子性。

可见性:提到可见性,很多同学就会想到一个关键字 volatile ,没错,在多线程中,解决变量的可见性就是利用了volatile这个修饰词。

多线程变量不可见:当一个线程对一变量a修改后,还没有来得及将修改后的a值回写到主存,而被线程调度器中断操作(或收回时间片),然后让另一线程进行对a变量的访问修改,这时候,后来的线程并不知道a值已经修改过,它使用的仍旧是修改之前的a值,这样修改后的a值就被另一线程覆盖掉了。

多线程变量可见:被volatile修饰的成员变量在每次被线程访问时,都强迫从内存中重读该成员变量的值;而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存,这样在任何时刻两个不同线程总是看到某一成员变量的同一个值,这就是保证了可见性。

举个例子:

编写一个线程类:

public class MyThread4 implements Runnable {
    private boolean isRun = true;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    @Override
    public void run() {
        System.out.println("4开始运行了");
        while (isRun){
           
        }
    }
}

main()方法运行

MyThread4 myThread4 = new MyThread4();
        Thread thread = new Thread(myThread4);
        thread.start();
        try {
            Thread.sleep(100);
            myThread4.setRun(false);
            System.out.println("停了吗");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

看运行结果,会发现线程进入了死循环,myThread4.setRun(false);这行代码并没有使线程重新拿到 isRun 的赋值,这说明线程之间对同一个变量的操作是不可见的。

当我们把 isRun 加上volatile关键字时如下:

public class MyThread4 implements Runnable {
    private volatile boolean isRun = true;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    @Override
    public void run() {
        System.out.println("4开始运行了");
        while (isRun){
            
        }
    }
}

再次运行我们会发现,代码不会进入死循环,由此可见,关键字volatile 保障了线程之间变量的可见性。

拓展:当我们在while循环中加入有synchronized关键字修饰的方法时,比如System.out.println();这时代码也不会陷入死循环,因此synchronized除了保障了原子性外,其实也保障了可见性。因为synchronized无论是同步的方法还是同步的代码块,都会先把主内存的数据拷贝到工作内存中,同步代码块结束,会把工作内存中的数据更新到主内存中,这样主内存中的数据一定是最新的。

原文地址:https://www.cnblogs.com/bestxyl/p/8779970.html