Java多线程之内存可见性——volatile

来源:互联网 发布:淘宝网,挂烫机加热器 编辑:程序博客网 时间:2024/06/15 02:14

volatile关键字:

1.能够保证volatile变量的可见性
2.不能保证volatile变量的复合操作的原子性

volatile是通过加入内存屏障禁止重排序优化来实现内存可见性。
java中具体的屏障指令就不说了,可以自行搜索

通俗来说:
volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,
而当该变量发生变化时,又会强迫线程将线程将最新的值刷新到主内存。
这样任何时刻,不同的线程总能看到该变量的最新值。

那么volatile的读写操作的过程:

线程写volatile变量的过程:
1.改变线程工作内存中volatile变量的副本的值
2.将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
1.从主内存中读取volatile变量的最新值到线程的工作内存中
2.从工作内存中读取volatile变量的副本

public class Demo {    //由于volatile不具备原子性(同步),所以代码运行中出现小于500的情况    private volatile int number = 0;    public int getNumber() {        return this.number;    }    public void increase() {        try {            Thread.sleep(100);  //休眠是为了使实验结果更明显        } catch(InterruptedException e) {            e.printStackTrace();        }        this.number++;    }    public static void main(String args[]) {        final Demo demo = new Demo();        for(int i=0;i<500;i++) {            new Thread(new Runnable() {                //覆写run方法                public void run() {                    Demo.increase();                }            }).start();        }        //如果子线程都运行完了,主线程再继续往下执行        while(Thread.activeCount() > 1) {  //判断是否500个子线程是否执行完            Thread.yield();        }        System.out.println("number:"+Demo.getNumber());    }}/*简要分析:当number为100时,1.线程A读取number的值2.线程B读取number的值3.线程B执行加1操作4.线程B写入最新的number的值5.线程A执行加1操作6.线程A写入最新的number的值不难发现,两次number++只增加了1,这就是原因所在*/

解决方案即为保证变量的原子性即可:
1.使用synchronized关键字
2.使用ReentrantLock(java.util.concurrent.locks包下)
3.使用AtomicInteger(vava.util.concurrent.atomic包下)

解决代码:

//第一种方案,在numbe++加synchronized关键字public void increase() {    try {        Thread.sleep(100);    } catch(InterruptedException e) {        e.printStackTrace();    }    synchronized(this) {  //缩小锁粒度,没必要锁整个方法        this.number++;    }}
//第二种方案,使用lock锁import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class Demo {    //锁操作还是很强的,感兴趣可以深入了解下    private Lock lock = new ReentrantLock;    private int number = 0;    public int getNumber() {        return this.number;    }    public void increase() {        try {            Thread.sleep(100);        } catch(InterruptedException e) {            e.printStackTrace();        }        //这里的加锁解锁,相当于进入退出synchronized代码块        //同时也可以保证number的可见性,number++的原子性        lock.lock();        try {  //之所以加入try语句,是因为锁内部的操作可能会抛出异常            this.number++;        } finally {            lock.unlock();        }    }    //mian方法不变}

volatile使用的场合 :

1.对变量的写入操作不依赖其当前值

不满足:number++等依赖前一个数的值 等等
满足:boolean变量,记录温度变化的变量 等等

2.该变量没有包含在具有其他变量的不变式中

不满足:不变式 low < up 等

0 0
原创粉丝点击