volatile关键字理解

   JMM(JAVA内存模型)

 

 个人理解:

    <1> 假设现在有3个线程t1、t2、t3,需要对主内存中的shareVar进行加1操作,JMM规范要求:这3个线程分别从主内存中拷贝一份shareVar到自己的工作空间中(即m1、m2、m3各存在一份),然后三个线程可以进行加1操作,然后每当一个线程完成了加1操作,就立即把其工作内存中的数据刷新到主内存中,并且通知其它线程更新其工作空间中的值;

    <2> 特点: 

     可见性 : 共享数据在多个线程并发操作时,某个线程修改了共享数据,其它线程可以及时获取到更新通知;
     原子性 : 共享数据在多个线程并发操作时,能获取到最新的、正确的值;
     有序性 : 共享数据在多个线程并发操作时,不会因某些原因(如:指令重排等)导致共享数据结果出现不一致问题(典型例子:++操作)。

    <3> 目前实现JMM规范的技术:synchronized、volatile(不满足原子性)

          

volatile

// 满足JMM的可见性和有序性
import java.util.concurrent.TimeUnit;

/**
 * 测试volatile作用
 * JMM三大特性:  可见性;原子性;有序性; JMM是一种规范 volatile是一种轻量级的实现;
 * volatile满足: 可见性;有序性(禁止指令重排); 不满足原子性!!!
 * 本例测试无volatile场景:
 */
public class VolatileDemo {
    public int num = 0;
    public void add60() {
        this.num += 60;
    }

    public static void main(String[] args) {
        VolatileDemo vd = new VolatileDemo();
        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+":\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            vd.add60();
            System.out.println(Thread.currentThread().getName()+":\t num = " + vd.num);
        }).start();
        while (vd.num==0){

        }
        System.out.println(Thread.currentThread().getName()+":\t num="+vd.num);
    }
}
VolatileNotSafeDemo.java
import java.util.concurrent.atomic.AtomicInteger;

/**
 * volatile 未能保证原子性 demo
 * 测试用例: num=0, 20个线程,每个线程对num加1(执行1000次)
 */
class MyData {
    volatile int num = 0;
    public void addPlusPlus(){
        num++;
    }
    AtomicInteger ai = new AtomicInteger();
    public void addMyAtomic(){
        ai.getAndIncrement();
    }
}

public class VolatileNotSafeDemo {
    volatile int num = 0;
    public void addPlusPlus(){
        num++;
    }
    public static void main(String[] args) {
        MyData vnd = new MyData();
        for(int i=0;i<20;i++) {
            new Thread(()->{
                for(int j=0;j<1000;j++){
                    vnd.addPlusPlus();
                }
            },String.valueOf(i)).start();
        }
        // main线程 && gc线程 : 此时代表所有线程(除前述的两个)执行完毕
        while(Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\t num="+vnd.num);
    }
}

解决volatile不满足原子性的方案

  1. 使用synchronized关键字
  2. 使用JUC包下面的Atomic类(如AtomicInteger) ,解决++数据不一致问题
    VolatileSafeWithAtomicDemo.java
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * volatile 不使用synchronized 保证原子性 demo
     * 测试用例: num=0, 20个线程,每个线程对num加1(执行1000次)
     */
    class MyData2 {
        volatile int num = 0;
        public void addPlusPlus(){
            num++;
        }
        AtomicInteger ai = new AtomicInteger();
        public void addMyAtomic(){
            ai.getAndIncrement();
        }
    }
    public class VolatileSafeWithAtomicDemo {
        public static void main(String[] args) {
            MyData2 vnd = new MyData2();
            for(int i=0;i<20;i++) {
                new Thread(()->{
                    for(int j=0;j<1000;j++){
                        vnd.addPlusPlus();
                        // 保证原子性
                        vnd.addMyAtomic();
                    }
                }).start();
            }
            // main线程 && gc线程 : 此时代表所有线程(除前述的两个)执行完毕
            while(Thread.activeCount()>2){
                Thread.yield();
            }
            System.out.println(Thread.currentThread().getName()+" int type ,finally number value: \t num="+vnd.num);// num=19991
            System.out.println(Thread.currentThread().getName()+" AtomicInteger type, finally number value :\t num="+vnd.ai);// num=20000
        }
    }

 

posted @ 2021-03-17 11:39  lvlin241  阅读(64)  评论(0)    收藏  举报