synchronized同步锁原理
介绍
- 在多线程操作中volatile关键字可以保证共享变量的内存可见性, 但是并不能保证操作的原子性, 这时候就需要用到锁, synchronized同步锁是java关键字, 是内置的语言实现.
- synchronized加锁和线程结束或异常锁的释放过程由JVM进行控制
- synchronized关键字可以使用在方法和同步代码块中, 不同的使用方式, 锁的结果是不同的
- 重量级锁 + 可重入
synchronized底层原理
1.代码示例
package com.liuzhihang.tool.java;
/**
* @author liuzhihang
* @date 2018/06/11 16:05
*/
public class SynchronizedTest {
private int i;
private int j;
public void syncTest1() {
synchronized (this) {
i++;
}
}
public synchronized void syncTest2() {
j++;
}
}
2.使用 javap -v SynchronizedTest.class 查看代码的对应字节码如下:
$ javap -v SynchronizedTest.class
Classfile /C:/Users/liuzhihang/Desktop/SynchronizedTest.class
Last modified 2018-7-10; size 518 bytes
MD5 checksum ba48def77b226e7b9ac28121ec423c16
Compiled from "SynchronizedTest.java"
public class com.liuzhihang.tool.java.SynchronizedTest
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
// 常量池省略
{
// 构造方法省略
public void syncTest1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: dup
6: getfield #2 // Field i:I
9: iconst_1
10: iadd
11: putfield #2 // Field i:I
14: aload_1
15: monitorexit
16: goto 24
19: astore_2
20: aload_1
21: monitorexit
22: aload_2
23: athrow
24: return
Exception table:
// 省略代码
public synchronized void syncTest2();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #3 // Field j:I
5: iconst_1
6: iadd
7: putfield #3 // Field j:I
10: return
LineNumberTable:
line 22: 0
line 23: 10
}
SourceFile: "SynchronizedTest.java"
3.结论
- 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令, 其中有两个 monitorexit 因为不能确保是正常结束还是异常结束, 所以另一个是用来确保异常结束时释放 monitor指令.
- 同步方法时使用的是 flags中的 ACC_SYNCHRONIZED 来标识该方法为同步方法, JVM在调用该方法时便会执行相应的同步调用.
- 每个线程都维护自己的监视器(monitor), 只要是同步调用进行相关操作时要先获得 monitor, 否则将被阻塞
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员小航
评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果