java并发底层实现原理

JMM模型:每个线程有一个本地内存,共享变量存储在主内存中。

Volatile:将一个变量声明为Volatile可以保证变量的可见性。即如果线程A写了一个变量的值,它可能只是写到了本地内存,而当线程B去读这个变量的时候,可能会读取不到刚刚线程A写的值。而如果将变量声明为Volatile,则可以在写的同时将本地内存中的共享变量值全部刷新到主内存中。这样线程B就可以读取到A线程写的值了。具体如下:

Volatile的内存语义:

当写一个Volatile变量时,JMM会把本地内存中的共享变量值刷新到主内存。

当读一个Volatile变量时,JMM会把线程对应的本地内存置为无效,然后从主内存中读取共享变量。

Sychronized锁也有同样的内存语义:

线程释放锁时,JMM会把本地内存中的共享变量值刷新到主内存。

线程获取锁时,JMM会把线程对应的本地内存置为无效,然后从主内存中读取共享变量。

同样的,ReentrantLock是通过Volatile变量实现的,因此也有同样的内存语义。CAS同时具有Volatile读和写的内存语义。

final域:线程A在构造函数中给一个普通变量的赋值,线程B随后读取这个对象引用并读取对象的普通变量的值。由于重排序规则,对普通变量的赋值可能出现在线程B读取对象引用和读取对象普通变量的值的后面。这样线程A所做的赋值操作对线程B就是不可见的。而如果在构造函数中对一个被声明为了final的变量赋值,则对它的赋值不允许被重排序到构造函数外,即线程B总是能读取到线程A对它赋予的值。

写final域的规则:JMM禁止编译器把final域的写重排序到构造函数之外。这个规则可以确保:在对象引用为任意线程可见之前,对象的final域已经被正确的初始化了。

读final域的规则:在一个线程中,初次读取对象引用与初次读取对象包含的final域,这俩个操作不能被重排序。

对于final域为引用类型,对final域里面的变量在构造函数内的赋值也不能被重排序到构造函数外。

对一个类进行实例化有三个步骤:

1:分配对象的内存空间,2:初始化对象,3:设置引用指向内存空间

其中2和3是可以重排序的。但如果将其声明为Volatile变量,则可以禁止2和3的重排序。

如下程序是不安全的:

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


如下程序是安全的:

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

java语言中的线程安全(根据安全程度排序):

1:不可变:对于被final域修饰的对象而言,只要其被构造出来了,则其外部可见状态永远不会改变。

2:绝对线程安全:不管运行时环境如何,都不需要任何同步措施。

3:相对线程安全:这是我们通常意义上的线程安全,需要保证这个对象单独的操作时线程安全的,我们在调用的时候不需要额外的保障措施。但对于一些特定顺序的连续调用,可能需要额外的同步手段。

4:线程兼容:对象本身不安全,可以通过额外同步手段来使用。

5:线程队列:怎么样都不可能线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值