JVM对高并发的支持-volatile

volatile解决可见性与重排序的问题

volatile是JVM提供的一个轻量级的同步机制

1. 保证可见性

volatile修饰的变量可以对所有线程立即可见

由JMM相关知识可知,当给变量加了volatile关键字后,当主内存中的变量被修改后,其他拷贝主内存中变量的线程会将之前拷贝的变量置为无效,重新从主内存中读取更新后的变量。

2. 禁止指令重排

java中,并非所有的语句都是原子性的(并非写成一行的语句就会原子执行)

JVM会对编写的代码进行一些额外的优化,但是原则是:不影响单线程程序的执行结果,即最后的执行结果肯定是保证相同的。

从一个单例模式开始说起

public class Singleton {
   private static Singleton instance = null;//多个线程共享instance
   private Singleton() {}
   public static Singleton getInstance() {
       if (instance == null){
           synchronized(Singleton.class){
              if (instance == null)
                  instance = new Singleton();
               }
         }
         return instance;
     }
}

ps:这段代码有问题,要不然不会总结volatile关键字了

instance = new Singleton();

上面这行代码不是一个原子性的操作,JVM在执行时大致拆分为以下三步:

  1. 分配内存地址、内存空间
  2. 实例化对象
  3. 将对象赋值为1分配好的内存

由于重排序,可能是1、3、2,单线程情况下并不会出异常,但是多线程:

线程X:执行到3(已被赋值了,不为null但是还未实例化),与此同时

线程Y:执行到

if (instance == null){
    
}
return instance;

发现instance不为null ,直接将该未实例化的对象返回,后续如果使用的话会造成未知的错误

======================================================================================

所以为了避免上述的问题,我们给instance加上volatile关键字,禁止JVM重排序

private volatile static Singleton instance = null;

这样子,这个单例模式才算是完整

注意:此处解决的问题为禁止重排序,但是volatile并不保证原子性,所以并不能保证线程安全

volatile工作原理?

内存屏障

在volatile写操作前,插入一个StoreStore屏障

在volatile写操作后,插入一个StoreLoad屏障

在volatile读操作前,插入一个LoadLoad屏障

在volatile读操作后,插入一个LoadStore屏障

注意:volatile并不是线程安全的

比如常见的“漏加”例子:

public class TestVolatile_1 {
	public static volatile int num = 0;
	public static void main(String[] args) throws Exception {
		for (int i = 0; i <100; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					for (int i = 0; i <20000; i++) {
						 num++;//num++不是一个原子性操作
					}
				}
			}).start();
		}
		Thread.sleep(3000);//休眠3秒,确保创建的100个线程都已执行完毕
		System.out.println(num);
	 }
}

num++被多个线程同时执行,造成漏加

可以使用JUC中的原子类来保证线程安全

public class TestVolatile_2 {
	public static AtomicInteger  num = new AtomicInteger(0);
	public static void main(String[] args) throws Exception {
	   for (int i = 0; i <100; i++) {
		  new Thread(new Runnable() {
			 @Override
			 public void run() {
				for (int i = 0; i <20000; i++) {
				  num.incrementAndGet() ;// num自增,功能上相当于int类型的num++操作
				}
			 }
		   }).start();
		}
	   Thread.sleep(3000);//休眠3秒,确保创建的100个线程都已执行完毕
	   System.out.println(num);
	}
}

AtomicInteger实现了CAS算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值