文章目录
- 前言
- 一、实现原理
- 二、代码演示
- 三、原理分析
- 总结
前言
synchronized 是 Java中重量级别的锁,学习多线程并发的同学肯定都不陌生,那么它是怎么确保多线程并发操作的原子性呢?
这篇文章带你深入了解其中的原理。
一、实现原理
保证同一时间只有一个线程操作共享变量,synchronized保证只有一个线程能拿到锁,能够进入同步代码块执行。
二、代码演示
需求:对一个共享变量num进行+1操作,开启5个线程同时操作,每个线程只能加1000,结果要求加到5000。
- 代码如下:
package com.study.syn;
import java.util.ArrayList;
import java.util.List;
/**
* synchronized 原子性原理分析
*/
public class Automicity {
/**
* 全局静态变量(也是共享变量)
*/
private static int num = 0;
/**
* 全局锁
*/
private static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
// 对num进行1000次的++操作
Runnable increment = () -> {
for (int i = 0; i < 1000; i++) {
synchronized (obj) {
num++;
}
}
};
// 开启多线程
List<Thread> list = new ArrayList<>();
// 开启5个线程
for (int i = 0; i < 5; i++) {
Thread t = new Thread(increment);
t.start();
list.add(t);
}
for(Thread t : list) {
t.join();
}
System.out.println("num:" + num);
}
}
- 我们可以先试试不在num++的部分加锁,如图所示部分:
- 让我们多执行几次来看看结果:
- 可以看到,我们执行了3次,只有一次结果是符合我们的预期的。
- 我们再把代码还原,看看加了synchronized锁之后会怎样:
- 让我们一起看看执行结果:
- 可以看到加锁后三次结果都符合我们的预期。
三、原理分析
- 想要了解其中的原理,我们需要查看class文件的汇编的结果
- 找到class文件,执行如下命令:
javap -p -v xxx.class
- 找到num++ 那段代码:
- 我们再把同步锁synchronized去掉,再执行一次汇编,看看有什么不同:
- 从以上可以对比出,加了synchronized之后多出了两个行代码,分别是:
- 代码块开始部分:monitorenter
- 代码块结束部分:monitorexit
- 关于monitor的实现大家可以看看hotspot的源码,是C++写的(OpenJDK8源码下载地址)
- monitro中有当前拥有锁的线程信息:
- 也就是说只有这个_owner线程具有执行monitor代码块的权限,从而确保了共享变量同一时间只有一个线程在操作,从而保证了原子性。
总结
Java中synchronized通过控制同一时间可进入代码块的线程有且只能有一个,来保证其原子性。