Java关键词synchronized解读
1 引入Synchronized
- Synchronized是java虚拟机为线程安全而引入的。
- 互斥同步是一种最常见的并发正确性的保障手段。同步是指在多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一条线程使用。
- synchronized是最基本的互斥同步手段,它是一种块结构的同步语法。
- synchronized修饰代码块,无论该代码块正常执行完成还是发生异常,都会释放锁
synchronized对线程访问的影响:
- 被synchronized修饰的同步块在持有锁的线程执行完毕并释放锁之前,会阻塞其他线程的进入。
- 被synchronized修饰的同步块对同一条线程是可重入的
2 Synchronized的使用
可以作用在方法上或者方法里的代码块:
- 修饰方法,包括实例方法和静态方法
- 修饰方法里的代码块,这时需要一个引用作为参数。
- Synchronized作用地方不同,产生的锁类型也不同,分为对象锁和类锁
2.1 对象锁
Synchronized修饰实例方法或者代码块(锁对象不是*.class),此时生产对象锁。多线程访问该类的同一个对象的sychronized块是同步的,访问不同对象不受同步限制。
2.1.1 Synchronized修饰实例方法
public static void main(String[] args){
TempTest tempTest = new TempTest();
Thread t1 = new Thread(() -> {
tempTest.doing(Thread.currentThread().getName());
});
Thread t2 = new Thread(() -> {
tempTest.doing(Thread.currentThread().getName());
});
t1.start();
t2.start();
}
//同一时刻只能被一个线程调用
private synchronized void doing(String threadName){
for(int i=0;i<3;i++){
System.out.println("current thread is : "+threadName);
try {
Thread.sleep(50);
} catch (InterruptedException e) {}
}
}
运行结果:
current thread is : Thread-0
current thread is : Thread-0
current thread is : Thread-0
current thread is : Thread-1
current thread is : Thread-1
current thread is : Thread-1
2.1.2 Synchronized修饰代码块
public class SynchronizedObjectLock implements Runnable {
static SynchronizedObjectLock instence = new SynchronizedObjectLock();
@Override
public void run() {
// 同步代码块形式:锁为this,两个线程使用的锁是一样的,线程1必须要等到线程0释放了该锁后,才能执行
synchronized (this) {
System.out.println("我是线程" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束");
}
}
public static void main(String[] args) {
Thread t1 = new Thread(instence);
Thread t2 = new Thread(instence);
t1.start();
t2.start();
}
}
运行结果:
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束
2.2 类锁
synchronize修饰静态方法或指定锁对象为Class,此时产生类锁。多线程访问该类的所有对象的sychronized块是同步的,
2.2.1 synchronize修饰静态方法
public static void main(String[] args){
TempTest tempTest1 = new TempTest();
TempTest tempTest2 = new TempTest();
//虽然创建了两个TempTest实例,但是依然是调用同一个doing方法(因为是个static);因此doing还是会依次执行
Thread t1 = new Thread(() -> tempTest1.doing(Thread.currentThread().getName()));
Thread t2 = new Thread(() -> tempTest2.doing(Thread.currentThread().getName()));
t1.start();
t2.start();
}
//修饰静态方法,则是类锁;
private static synchronized void doing(String threadName){
for(int i=0;i<3;i++){
System.out.println("current thread is : "+threadName);
try {
Thread.sleep(50);
} catch (InterruptedException e) {}
}
}
运行结果:有序输出 【如果去掉static ,则线程会交替执行doing】
current thread is : Thread-0
current thread is : Thread-0
current thread is : Thread-0
current thread is : Thread-1
current thread is : Thread-1
current thread is : Thread-1
2.2.2 synchronize指定锁对象为Class
public class SynchronizedObjectLock implements Runnable {
static SynchronizedObjectLock instence1 = new SynchronizedObjectLock();
static SynchronizedObjectLock instence2 = new SynchronizedObjectLock();
@Override
public void run() {
// 所有线程需要的锁都是同一把
synchronized(SynchronizedObjectLock.class){
System.out.println("我是线程" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束");
}
}
public static void main(String[] args) {
Thread t1 = new Thread(instence1);
Thread t2 = new Thread(instence2);
t1.start();
t2.start();
}
}
结果:
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束
3 Synchronized原理分析
3.1 虚拟机如何辨别和处理synchronized
- 虚拟机可以从常量池中的方法表结构中的ACC_ SYNCHRONIZED访问标志区分一个方法是否是同步方法。
- 当调用方法时,调用指令将会检查方法的ACC_ SYNCHRONIZED访问标志是否设置,如果设置了,执行线程将先持有同步锁,然后执行方法,最后在方法完成时释放同步锁。
- 在方法执行期间,执行线程持有了同步锁,其他任何线程都无法再获得同一个锁。
- 如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理此异常,那这个同步方法所持有的锁将在异常抛到同步方法之外时自动释放。
3.2 虚拟机对synchronized的编译处理
以下代码:
public class Foo {
void onlyMe(Foo f) {
synchronized(f) {
doSomething();
}
}
private void doSomething(){ }
}
编译后,这段代码生成的字节码序列如下:

- synchronized关键字经过Javac编译之后,会在同步块的前后生成monitorenter和monitorexit两个字节码指令。
- 指令含义:monitorenter:获取对象的锁;monitorexit:释放对象的锁
- 执行monitorenter指令时,首先尝试获取对象的锁。如果对象没被锁定,或者当前线程已经持有了对象的锁,就把锁的计数器的值增加1
- 执行monitorexit指令时,将锁计数器的值减1,一旦计数器的值为零,锁随即就被释放
- 如果获取对象锁失败,那当前线程阻塞等待,直到锁被释放。
- 为了保证在方法异常完成时monitorenter和monitorexit指令依然可以正确配对执行,编译器会自动产生一个异常处理程序,它的目的就是用来执行monitorexit指令。
3.3 虚拟机执行加锁和释放锁的过程
那么重点来了到这里,有几个问题需明确:
- 什么叫对象的锁?
- 如何确定锁被线程持有?
- 执行monitorenter后,对象发生什么变化?
- 锁计数值保存在哪里,如何获取到?
1. 什么叫对象的锁?
对象的内存结构参考:2 Java内存层面的对象认识
- 锁,一种可以被读写的资源,对象的锁是对象的一部分。
- 对象的结构中有部分称为对象头。
- 对象头中有2bit空间,用于存储锁标志,通过该标志位来标识对象是否被锁定。
2. 如果确定锁被线程持有?
- 代码即将进入同步块的时,如果锁标志位为“01”(对象未被锁定),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录的空间,存储锁对象Mark Word的拷贝。(线程开辟空间并存储对象头)
- 虚拟机将使用CAS操作尝试把对象的Mark Word更新成指向锁记录的指针(对象头的mw存储指向线程“锁记录”中的指针)
- 如果CAS操作成功,即代表该线程拥有了这个对象的锁,并且将对象的锁标志位转变为“00”
- 如果CAS操作失败,那就意味着至少存在一条线程与当前线程竞争获取该对象的锁。虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是,说明当前线程已经拥有了这个对象的锁,那直接进入同步块继续执行,否则就说明这个锁对象已经被其他线程抢占。
- 解锁过程:CAS操作把线程中保存的MW拷贝替换回对象头中。假如能够成功替换,那整个同步过程就顺利完成了;如果替换失败,则说明有其他线程尝试过获取该锁,就要在释放锁的同时,唤醒被挂起的线程。
3 执行monitorenter后,对象发生什么变化?
- 对象的锁标志位转变为“00”
- 拥有对象锁的线程开辟了新空间,保存了对象的Mark Word信息
- 对象的Mark Word保存了线程的锁记录空间的地址拷贝
4 锁计数值保存在哪里?
我还没搞懂。
monitorenter指令执行的过程:

4 Synchronized与Lock
synchronized的缺陷
- 在多线程竞争锁时,当一个线程获取锁时,它会阻塞所有正在竞争的线程,这样对性能带来了极大的影响。
- 挂起线程和恢复线程的操作都需要转入内核态中完成,上下文切换需要消耗很大性能。
- 效率低:锁的释放情况少,只有代码执行完毕或者异常结束才会释放锁;试图获取锁的时候不能设定超时,不能中断一个正在使用锁的线程,相对而言,Lock可以中断和设置超时
- 不够灵活:加锁和释放的时机单一,每个锁仅有一个单一的条件(某个对象),相对而言,读写锁更加灵活
5 使用Synchronized有哪些要注意的
- 锁对象不能为空,因为锁的信息都保存在对象头里
- 作用域不宜过大,影响程序执行的速度,控制范围过大,编写代码也容易出错
- 在能选择的情况下,既不要用Lock也不要用synchronized关键字,用java.util.concurrent包中的各种各样的类,如果有必要,使用synchronized关键,因为代码量少,避免出错
- synchronized实际上是非公平的,新来的线程有可能立即获得执行,而在等待区中等候已久的线程可能再次等待,这样有利于提高性能,但是也可能会导致饥饿现象。
Java关键词synchronized解读的更多相关文章
- JAVA关键词synchronized的作用
记下来,很重要. Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchron ...
- 转载:Java Lock机制解读
Java Lock机制解读 欢迎转载: https://blog.csdn.net/chengyuqiang/article/details/79181229 1.synchronized synch ...
- Java I/O解读与使用实例
林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 摘要:本文主要讲解了Java I/O解读与使用实例. 一.I/O基本概念 I/O全称是Inpu ...
- Java 多线程 —— synchronized关键字
java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...
- Java的synchronized关键字:同步机制总结
JAVA中synchronized关键字能够作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块.搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程 ...
- JAVA多线程synchronized详解
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 当两个并发线程访问同一个对象object中的这个synchronized(this)同 ...
- java中synchronized的用法详解
记下来,很重要. Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchron ...
- java中synchronized的使用方法与具体解释
Java语言的keyword.当它用来修饰一个方法或者一个代码块的时候,可以保证在同一时刻最多仅仅有一个线程运行该段代码. 一.当两个并发线程訪问同一个对象object中的这个synchronized ...
- java同步synchronized
java同步synchronized volatile仅仅用来保证该变量对所有线程的可见性,但不保证原子性. 看下面的这段代码: /** * * @author InJavaWeTrust * */ ...
- java中synchronized关键字分析
今天我们来分析一下java中synchronized关键字.首先来看一段java代码:(本地编译环境为mac,jdk1.8的环境) Demo.java package com.example.spri ...
随机推荐
- 洛谷P3243 [HNOI2015]菜肴制作 (拓扑排序/贪心)
这道题的贪心思路可真是很难证明啊...... 对于<i,j>的限制(i必须在j之前),容易想到topsort,每次在入度为0的点中选取最小的.但这种正向找是错误的,题目要求的是小的节点尽量 ...
- POJ3237 Tree (树链剖分)
通过打懒标记实现区间取反,和线段树基本操作都差不多. 本题还是一道边权化为点权的问题. 200行巨长代码: 1 #include<cstdio> 2 #include<cstring ...
- 三十一、kubernetes网络介绍
Kubernetes 网络介绍 Service是Kubernetes的核心概念,通过创建Service,可以为一组具有相同功能的容器应用提供一个统一的入口地址,并且将请求负载分发到后端的各个容器应用上 ...
- Linux学习环境搭建流程
Linux学习环境搭建 Vmware安装 VMware下载:https://www.vmware.com/go/getworkstation-win 运行安装程序,该重启安装驱动就重启,不需要就下一步 ...
- Redisson源码解读-公平锁
前言 我在上一篇文章聊了Redisson的可重入锁,这次继续来聊聊Redisson的公平锁.下面是官方原话: 它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程.所有请 ...
- Java8新特性—四大内置函数式接口
Java8新特性--四大内置函数式接口 预备知识 背景 Lambda 的设计者们为了让现有的功能与 Lambda 表达式良好兼容,考虑了很多方法,于是产生了函数接口这个概念. 什么是函数式接口? 函数 ...
- networkQuality
基本使用 networkQuality 是一个命令行工具,需要使用「终端」App(或者你首选的其他终端模拟器)运行.方法是: 首先,点按「程序坞」(Dock)中的「启动台」(LaunchPad)图标, ...
- Linux下安装 SkyWalking 分布式追踪系统
Linux下安装 SkyWalking 分布式追踪系统 1.SkyWalking简介 1.1 SkyWalking介绍 SkyWalking项目是由华为大牛吴晟开源的个人项目,目前已经加入Apache ...
- C++ undered_map哈希表用法——leetcode两数之和
undered_map 头文件:#include<undered_map> 创建表undered_map<key,value> Map_name; 插入元素 a[key]=va ...
- 让 Serverless 更普惠,阿里云函数计算 FC 宣布全面降价,最大幅度达 37.5%
11月5日,2022 杭州 · 云栖大会上,阿里云宣布函数计算 FC 开启全面降价,vCPU 单价降幅** 11%,其他的各个独立计费项最高降幅达 37.5%**. 本次云栖大会上,阿里云智能总裁张建 ...