多线程与高并发

本文详细探讨了Java中的volatile关键字和synchronized关键字的作用。volatile确保线程间变量可见性和禁止指令重排序,适用于轻量级同步场景。在单例模式实现中,volatile配合双重检查锁定(DCL)可以实现线程安全的懒汉式单例,但无法保证原子性。文章通过实例展示了volatile无法替代synchronized的原因,强调了两者在并发控制中的不同角色。
摘要由CSDN通过智能技术生成

volatile关键字和synchronized

volatile作用

保证线程可见性(MESI 缓存一致性协议)

禁止指令重排序(CPU)

volatile应用

单例模式
(饿汉式)单例模式可以保证线程安全只创建一次。

public class DCL {
   
   private static DCL dcl = new DCL();
   
   private DCL(){}
   
   public DCL getInstance(){
   	return dcl;
   }
}

有些时候饿汉式单例模式,会在线程还没有开始调用这个对象的时候就已经创建了。所有为了解决这个问题引出了懒汉式单例模式。

public class DCL {
   private static DCL dcl;
   private DCL(){}
   public DCL getInstance(){
   	if (dcl == null){
   		dcl = new DCL();
   	}		
   	return dcl;
   }
}

但是这种实现会产生一个问题线程不安全在刚刚初始化程序的时候恰巧有几个线程同时获取到了dcl为 null的情况,那么他们会创建很多个DCL对象,与单例模式相违背了,于是引出了下面的写法:

public class DCL {
	private static DCL dcl;
	private DCL(){}
	public synchronized DCL getInstance(){
		if (dcl == null){
			dcl = new DCL();
		}
		return dcl;
	}
}

上面的实现虽然解决了创建线程只有一个,也实现了懒加载,但是 synchroized是重量级锁,如果这个类中包含很多业务逻辑那么会特别影响性能,于是我们采用锁细化:

public class DCL {
	private static DCL dcl;
	private DCL(){}
	public DCL getInstance(){
		if (dcl == null){
			synchronized(DCL.class){
				dcl = new DCL();
			}
		}
		return dcl;
	}
}

上面程序解决了锁细化的问题,但是产生了新的问题,同样在多线程时会产生多个对象,于是我们在此基础上进行改良:

public class DCL {
	private volatile static DCL dcl;
	private DCL(){}
	public DCL getInstance(){
		if (dcl == null){
			synchronized(DCL.class){
				if (dcl==null) {
					dcl = new DCL();
				}
			}
		}
		return dcl;
	}
}

我们进行两次判定也叫双重检核,并且我们在DCL静态对象上加上了 volatile关键字,这样在第一次判定时会在对象初始化后避免多个程序再次进行锁定,毕竟锁是非常影响性能的。第二次判定为如果再多线程都加载到dcl为 null 时,将调用锁,之后获取到锁的第一个线程才能初始化对象,之后的线程将再次判断 dcl 是否为空,因为第一个线程已经初始化了对象,所以直接将初始化的对象返回了。但是在程序在编译器编译之后,指令会被分成三步 1给指令申请内存。2给成员初始化。3是把这块内存赋值给 dcl 当第二个线程过来的时候第一个判定条件就会发现该值已经有了,就不会进入到锁的判定,这样的返回值就是错误的。于是我们使用 volatile关键字 来禁止指令重新排序。这样在初始化的时候就能保证创建线程时,就算是多个线程同时加载也不会乱,并且会走到竞争锁的地方。

volatile关键字不能保证原子性

public class T {
	volatile int count = 0;
	void m(){
		for (int i = 0; i < 10000; i++) {
			count++;
		}
	}
	public static void main(String[] args) {
		T t = new T();
		List<Thread> threads = new ArrayList<>();

		for (int i = 0; i < 10; i++) {
			threads.add(new Thread(t::m,"Thread"+i));
		}
		threads.forEach(Thread::start);
		threads.forEach((o)->{
			try {
				o.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});
		System.out.println(t.count);
	}
}

执行结果:
执行结果
程序执行之后并没有使count的值变成100000。所以volatile不可以代替synchronized。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值