锁 想必大家并不觉得陌生,起简单作用就是防止共享资源被同时访问,这时就是需要锁住。 锁就有许多种锁了,像syn cas reentrantlock等 我们这一章 主要来说说 synchronized。
在jdk1.5及之前时 我们的syn锁还是一个重量级锁,相对于lock就显得很笨重,但1.6后java从jvm层面对syn锁进行了优化,相比之前 效率还是很不错。
接下来我们将从以下几个点对Synchronized锁进行剖析。
- synchronized 的主要作用(特性)
- synchronized的主要用法
- synchronized同步原理
- synchronized同步概念
- synchronized优化
synchronized 的主要作用(特性)
- 原子性:原子性,大家想必都能想到事务的原子性,没错 底层原理类似 ,事务的原子性底层也是通过锁来实现。一个操作或多个操作要么全部执行,要么都不执行,中间不能被打断。
- 可见性:了解过volatile的也能知道什么是可见性,synchronized同样也具备可见性
- 有序性:同样和volatile一样具备有序性。
synchronized的主要用法
- 修饰实例方法: 作用于当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁
- 修饰静态方法: 也就是给当前类加锁,会作用于类的所有对象实例 ,进入同步代码前要获得 当前 class 的锁。因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管 new 了多少个对象,只有一份)。所以,如果一个线程 A 调用一个实例对象的非静态
synchronized方法,而线程 B 需要调用这个实例对象所属类的静态synchronized方法,是允许的,不会发生互斥现象,因为访问静态synchronized方法占用的锁是当前类的锁,而访问非静态synchronized方法占用的锁是当前实例对象锁。 - 修饰代码块 :指定加锁对象,对给定对象/类加锁。
synchronized(this|object)表示进入同步代码库前要获得给定对象的锁。synchronized(类.class)表示进入同步代码前要获得 当前 class 的锁
简单总结下:
synchronized 关键字加到 static 静态方法和 synchronized(class) 代码块上都是是给 Class 类上锁。
synchronized 关键字加到实例方法上是给对象实例上锁。
synchronized同步原理
数据同步需要依赖锁,那么锁的同步又依赖什么呢?synchronized给出的答案是在软件层面依赖jvm,juc.lock给出的答案是在硬件层面依赖特殊的cpu指令
1、synchronized 同步语句块原理
public class SynchronizedDemo {
public void method() {
synchronized (this) {
System.out.println("synchronized 代码块");
}
}
}
通过一系列技术(通过 JDK 自带的 javap 命令查看 SynchronizedDemo 类的相关字节码信息:首先切换到类的对应目录执行 javac SynchronizedDemo.java 命令生成编译后的 .class 文件,然后执行javap -c -s -v -l SynchronizedDemo.class。)我们可以找到以下图
我们从图中可以看到,同步语句块 使用的是monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置, monitorexit 指令则指明同步代码块的结束位置。
当执行 monitorenter 指令时,线程试图获取锁也就是获取 对象监视器 monitor 的持有权。
(在 Java 虚拟机(HotSpot)中,Monitor 是基于 C++实现的,由ObjectMonitor实现的。每个对象中都内置了一个 ObjectMonitor对象。另外,wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。)
在执行monitorenter时,会尝试获取对象的锁,如果锁的计数器为 0 则表示锁可以被获取,获取后将锁计数器设为 1 也就是加 1。
在执行 monitorexit 指令后,将锁计数器设为 0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。
2、synchronized 修饰方法原理
public class SynchronizedDemo2 {
public synchronized void method() {
System.out.println("synchronized 方法");
}
}
反编译一下

synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是 ACC_SYNCHRONIZED 标识,该标识指明了该方法是一个同步方法。
JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
不过两者的本质都是对对象监视器 monitor 的获取。
synchronized同步概念
1.对象头
在jvm中,一个对象被分层三个区域:对象头,实例数据和对齐填充。
2.Monitor对象
monitor可以翻译成监视器或者管程,是由JVM提供的,当线程执行到对象方法的临界区时,首先查看对 象头中是否已经有关联的Monitor对象,如果已经关联加入了monitor对象,则将当前线程添加到 monitor对象的entrylist中;如果没有关联monitor对象,则改变对象的对象头的mark word或者指向一 个monitor对象。如果已经指向了一个monitor对象,则表示monitor对象的拥有者变成了当前线程,当 前线程执行完成后monitor再释放线程,重新到entrylist中查找一个新的拥有者,一般非公平竞争。
synchronized优化
锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁。但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 16 天,点击查看活动详情




