Java synchronized原理分析

首先介绍下JMM主内存和本地内存的关系

JMM : Java内存模型,是一种规范
Java 作为高级语言,屏蔽了cpu多层缓存这些细节,用 JMM 定义了一套读写内存数据的规范,虽然不再需要关心一级缓存和二级缓存的问题,但是 JMM 抽象了主内存和本地内存的概念
主内存和本地内存的关系:
JMM 有以下规定:
1) 所有的变量都存储在主内存中,同时每个线程也有自己独立的工作内存,工作内存中的变量内容是主内存中的拷贝
2) 线程不能直接读写主内存中的变量,而是只能操作自己工作内存中的变量,然后再同步到主内存中
3) 主内存是多个线程共享的,但线程间不共享工作内存,如果线程间需要通信,必须借助主内存中转来完成

所有的共享变量存在于主内存中,每个线程有自己的本地内存,而线程读写数据共享也是通过本地内存交换的,
所以才导致了可见性问题

锁的内存语义

synchronized的底层是使用操作系统的mutex lock实现的。

  • 内存可见性:同步快的可见性是由“如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量前需要重新执行load或assign操作初始化变量的值”、“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store和write操作)”这两条规则获得的。
  • 操作原子性:持有同一个锁的两个同步块只能串行地进入

锁的内存语义:

  • 当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中
  • 当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量

锁释放和锁获取的内存语义:

  • 线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
  • 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
  • 线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息

synchronized锁

synchronized用的锁是存在Java对象头里的。

JVM基于进入和退出Monitor对象来实现方法同步和代码块同步。代码块同步是使用monitorenter和monitorexit指令实现的,monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。

根据虚拟机规范的要求,在执行monitorenter指令时,首先要去尝试获取对象的锁,如果这个对象没被锁定,或者当前线程已经拥有了那个对象的锁,把锁的计数器加1;相应地,在执行monitorexit指令时会将锁计数器减1,当计数器被减到0时,锁就释放了。如果获取对象锁失败了,那当前线程就要阻塞等待,直到对象锁被另一个线程释放为止。

注意两点:

1、synchronized同步快对同一条线程来说是可重入的,不会出现自己把自己锁死的问题;

2、同步块在已进入的线程执行完之前,会阻塞后面其他线程的进入。

扫描二维码关注公众号,回复: 12455489 查看本文章

https://zhuanlan.zhihu.com/p/29866981

猜你喜欢

转载自blog.csdn.net/KAITUOZHEMJ/article/details/112343570