01、volatile+JMM

volatile

volatile是java虚拟机提供的轻量级的同步机制
1、保证可见性
在多线程中,每个线程不能直接修改主内存中(这里说的主内存就是内存条)的数据,而是要先把主内存中的数据拷贝到自己的工作内存中,线程在各自的工作内存中修改数据,然后把修改后的数据写入到主内存中,可见性指的是在主内存总的数据被其他线程重新写入的时候能够读取到写入后的数据
volatile可见性代码验证
没加volatile修饰

package com.lxj.interview2;

import java.util.concurrent.TimeUnit;

/**
 * 验证volatile的可见性
 * 1、假如int num = 0;中没有添加volatile修饰,没有可见性
 * */
public class VolatileDemo {
    public static void main(String[] args) {//main是一切方法的运行入口
        MyData myData = new MyData();//资源类
        new Thread(() ->{
            System.out.println(Thread.currentThread().getName()+"\t come in");
            try {
                //暂停一会儿线程
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.changeNum();
            System.out.println(Thread.currentThread().getName()+"\t updated num value: "+myData.num);
        },"aaa").start();

        //第二个线程就是main线程
        while (myData.num == 0){
            //main线程一直在这里等待,直到num不再为0
        }
        System.out.println(Thread.currentThread().getName()+"\t mission is over");
    }
}

class MyData{
    int num = 0;
    public void changeNum(){
        this.num = 60;
    }
}

运行结果

aaa	 come in
aaa	 updated num value: 60

并且任务是一直在进行中的

加vola修饰后

package com.lxj.interview2;

import java.util.concurrent.TimeUnit;

/**
 * 验证volatile的可见性
 * 1、假如int num = 0;中没有添加volatile修饰,没有可见性
 * */
public class VolatileDemo {
    public static void main(String[] args) {//main是一切方法的运行入口
        MyData myData = new MyData();//资源类
        new Thread(() ->{
            System.out.println(Thread.currentThread().getName()+"\t come in");
            try {
                //暂停一会儿线程
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.changeNum();
            System.out.println(Thread.currentThread().getName()+"\t updated num value: "+myData.num);
        },"aaa").start();

        //第二个线程就是main线程
        while (myData.num == 0){
            //main线程一直在这里等待,直到num不再为0
        }
        System.out.println(Thread.currentThread().getName()+"\t mission is over");
    }
}

class MyData{
    volatile int num = 0;
    public void changeNum(){
        this.num = 60;
    }
}

运行结果

aaa	 come in
aaa	 updated num value: 60
main	 mission is over

任务结束

2、不保证原子性
不保证原子性案例

package com.lxj.interview2;

/**
 * 一、验证volatile的可见性
 * 1、假如int num = 0;中没有添加volatile修饰,没有可见性
 * 2、添加volatile,可以解决可见性问题
 * 二、验证volatile不支持原子性
 * 1、原子性是什么意思
 *      不可分割,完整性,也即某个线程正在做某个业务时,组成这个业务的过程,要么同时成功,要么同时失败
 * 2、volatile不保证原子性案例
 *
 * */
public class VolatileDemo {
    public static void main(String[] args) {//main是一切方法的运行入口
        MyData myData = new MyData();
        for (int i = 0;i < 20; i++){
            new Thread(() ->{
                for (int j = 0;j < 1000;j++){
                    myData.addValue();
                }
            },String.valueOf(i)).start();
        }
        while(Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\t finally num is: "+myData.num);
    }

}

class MyData{
    volatile int num = 0;
    //请注意,此时num前面是加了volatile修饰的,volatile不保证原子性
    public void addValue(){
        num ++;
    }
}

运行结果

main	 finally num is: 16153

如何解决volatile不保证原子性

package com.lxj.interview2;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 一、验证volatile的可见性
 * 1、假如int num = 0;中没有添加volatile修饰,没有可见性
 * 2、添加volatile,可以解决可见性问题
 * 二、验证volatile不支持原子性
 * 1、原子性是什么意思
 *      不可分割,完整性,也即某个线程正在做某个业务时,组成这个业务的过程,要么同时成功,要么同时失败
 * 2、volatile不保证原子性案例
 * 3、如何解决volatile不保证原子性
 *      加sync,在调用的方法addValue()中加入synchronized即可
 *      使用juc下面的AtomicInteger
 * */
public class VolatileDemo {
    public static void main(String[] args) {//main是一切方法的运行入口
        MyData myData = new MyData();
        for (int i = 0;i < 20; i++){
            new Thread(() ->{
                for (int j = 0;j < 1000;j++){
                    myData.addValue();
                    myData.addValueAtom();
                }
            },String.valueOf(i)).start();
        }
        while(Thread.activeCount()>2){
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName()+"\t int finally num is: "+myData.num);
        System.out.println(Thread.currentThread().getName()+"\t AtomicInteger finally num is: "+myData.atomicInteger);
    }

}

class MyData{
    volatile int num = 0;
    //请注意,此时num前面是加了volatile修饰的,volatile不保证原子性
    public void addValue(){
        num ++;
    }
    AtomicInteger atomicInteger = new AtomicInteger();
    public void addValueAtom(){
        atomicInteger.getAndIncrement();
    }
}

运行结果

main	 int finally num is: 15660
main	 AtomicInteger finally num is: 20000

3、禁止指令重排
加了volatile后可以禁止指令重排,指令重排是指数据在没有依赖的情况下会出现后面的指令先执行的情况,单线程不会有影响,在多线程环境下指令重排会导致最终运行的结果有问题。

jmm

java内存模型
1、可见性(这里的可见性原理和volatile的可见性原理是一样的)
2、原子性
3、volatile中演示的可见性和原子性
4、有序性

单例模式在多线程下可能存在不安全问题

package com.lxj.interview2;

public class SingletonDemo {
    //加volatile可以防止线程不安全问题(即使在双端检测机制下,当线程数量超过了一定值后也会出现线程不安全,也就是这个单例模式会出现多实例,导致这个问题的原因是指令重排,这时候加上volatile就可以防止这个问题出现)
    private static volatile SingletonDemo instance = null;
    private SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+"\t 我是构造方法SingletonDemo()");
    }
    //DCL(double check local 双端检测机制)
    public static SingletonDemo getInstance(){
        if (instance == null){
            synchronized (SingletonDemo.class){
                if (instance == null){
                    instance = new SingletonDemo();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        for (int i = 0;i< 20;i++){
            new Thread(() ->{
               SingletonDemo.getInstance();
            },String.valueOf(i)).start();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值