单例多线程中,举例说明synchronized关键字和volatile关键字的差别

本文对比分析了volatile和synchronized在Java中的应用。volatile保证了线程可见性和禁止指令重排序,但不保证原子性,适用于简单变量的并发访问。而synchronized提供了原子性,确保了线程安全,但可能导致阻塞,影响性能。通过Singleton实例化过程展示了两者的区别,volatile在多线程环境下可能创建多个实例,而synchronized确保唯一实例。
摘要由CSDN通过智能技术生成

synchronized属于对象锁可以保证多线程的安全,但是会造成阻塞比较严重。即使对synchronized进行优化,也会留有一定的阻塞,对计算机的性能不太友好。

volatile关键字相当于内存和cpu之间的放哨员,如果有synchronized修饰的变量a,在多线程同时访问变量a并进行修改成b时,如果有某个线程已经把a改为b了,就会返回给内存,并提醒其他核的cpu:“变量a已改,无需再改”。

volatile实例
 

package practice;
 
    //volatile关键字作用(修饰类中属性)
	//1.保证线程的可见性()
	//2.禁止指令重排序
	//3.但不能保证其原子性
 
class Singleton06{
public Singleton06() {
	System.out.println("instance");
}
//volatile不能修饰方法一般都是修饰变量属性
private static volatile Singleton06 instance ;
public static Singleton06 getInstance() {
	if (instance==null) {
		instance = new Singleton06();
		}
	return instance;
	}
}
 
public class TestObjectInstance_practice {
	public static void main(String[] args) {
		//记录当前时间
		long a=System.currentTimeMillis();
		//执行多线程访问
		doTestManyThread();
		//输出执行时间
		System.out.println("\r<br>执行耗时 : "+
				(System.currentTimeMillis()-a)/1000f+" 秒 ");
	}
 
	private static void doTestManyThread() {
		class Task implements Runnable {
			@Override
			public void run() {
				Singleton06.getInstance();
				
			}
		}
        //调用一千个线程访问获取instance变量
		for (int i = 0; i <1000; i++) {
			new Thread(new Task()).start();
			
		}
	}
}

运行结果: 

 synchronized例子:

package practices;
 
//synchronized 保证代码的原子性(不能同时有多个线程在这代码)
//synchronized 要让多个线程在这个代码块上顺序执行
//此设计虽然保证了线程安全,性能却大大减低
//重构方法的设计,优化synchronized(既要保证线程的安全,又减少阻塞)但是在高并发中阻塞依然明显
 
class Singleton03{
public Singleton03() {
	System.out.println("创建对象instance");
}
private static Singleton03 instance ;
public static Singleton03 getInstance() {
	if (instance==null) {
		synchronized (Singleton03.class) {
			if(instance==null) {
				instance = new Singleton03();
			}
		}
	}
	return instance;
}
 
public class TestObjectInstance_practice {
	public static void main(String[] args) {
		//记录当前时间
		long a=System.currentTimeMillis();
		//执行多线程访问
		doTestManyThread();
		//输出执行时间
		System.out.println("\r<br>执行耗时 : "+
				(System.currentTimeMillis()-a)/1000f+" 秒 ");
	}
 
	private static void doTestManyThread() {
		class Task implements Runnable {
			@Override
			public void run() {
				Singleton03.getInstance();
				
			}
		}
		for (int i = 0; i <1000; i++) {
			new Thread(new Task()).start();
			
		}
	}
}

运行结果:

 

从两个运行结果可以看出,

volatile修饰后,创建了三个对象,所以没有确保原子性,但是运行时间却比synchronized少很多。

synchronized的运行结果,只创建一个instance对象,可以确保原子性,但是运行时间却比volatile长,造成一定的阻塞。

单纯使用 volatile 关键字是不能保证线程安全的

volatile 只提供了一种弱的同步机制,用来确保将变量的更新操作通知到其他线程
volatile 语义是禁用 CPU 缓存,直接从主内存读、写变量。表现为:更新 volatile 变量时,JMM 会把线程对应的本地内存中的共享变量值刷新到主内存中;读 volatile 变量时,JMM 会把线程对应的本地内存设置为无效,直接从主内存中读取共享变量
当把变量声明为 volatile 类型后,JVM 增加内存屏障,禁止 CPU 进行指令重排

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值