volatile和synchronized关键字

1.volatile关键字

 Java语言提供了一种稍弱的同步机制,即volatile变量,用来确保将变量的更新操作通知到其他线程。当把变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

当一个变量定义为 volatile 之后,将具备两种特性:

  1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存(详见:Java内存模型)来完成。

  2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;(什么是指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)。

注意:

volatile关键字不能保证原子性(在使用volatile修饰的字段时会检查其是否失效,如果在计算完成,写入时阻塞,唤醒之后不会再次判断volatile修饰的字段是否有效,而是直接写入)

2.synchronized关键字

synchronized关键字可以保证原子性,即只有一个线程可以进入到被synchronized修饰的逻辑中

2.1锁升级

 

2.2 synchronized与Monitor

每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针

monitor对象重要属性说明:

_owner:指向持有ObjectMonitor对象的线程

_WaitSet:存放处于wait状态的线程队列

_EntryList:存放处于等待锁block状态的线程队列

_recursions:锁的重入次数

_count:用来记录该线程获取锁的次数

假如当前线程A、B、C同时访问同步块。

假如A获取到锁,也就会将 monitor 对象中的 _owner 的值赋值为当前线程ID。B、C线程会进入EntryList中。count =1 , recursions=1。假如A线程第二次进入同步快,count = 2, recursions=2,当前线程退出时,count和recursions会减一,直到count=0, recursions=0时,说明线程A释放了monitor锁,然后会唤醒EntryList中的线程,EntryList线程会竞争monitor,竞争到了,和线程A的操作一致。

 

 

posted @ 2023-01-30 11:02  isalo  阅读(13)  评论(0编辑  收藏  举报