提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
1、synchronized的用法
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
1.1、修饰实例方法
synchronized修饰实例方法只需要在方法上加上synchronized关键字即可。
public synchronized void add(){
i++;
}
此时,synchronized加锁的对象就是这个方法所在实例的本身。
1.2、修饰静态方法
synchronized修饰静态方法的使用与实例方法并无差别,在静态方法上加上synchronized关键字即可
public static synchronized void add(){
i++;
}
此时,synchronized加锁的对象为当前静态方法所在类的Class对象。
1.3、修饰代码块
synchronized修饰代码块需要传入一个对象。
public void add() {f
synchronized (this) {
i++;
}
}
很明显,此时synchronized加锁对象即为传入的这个对象实例。
到这里不是道你是否有个疑问,synchronized关键字是如何对一个对象加锁实现代码同步的呢?如果想弄清楚,那就不得不先了解一下Java对象的对象头了。
2、synchronized底层实现原理
在Java代码中,我们只是使用了synchronized关键字就实现了同步效果。那他到底是怎么做到的呢?这就需要我们通过javap工具来反汇编出字节指令一探究竟了。
2.1、同步代码块
通过javap -v来反汇编下面的一段代码。
public void add() {
synchronized (this) {
i++;
}
}
可以得到如下的字节码指令
public class com.zhangpan.text.TestSync {
public com.zhangpan.text.TestSync();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void add();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter // synchronized关键字的入口
4: getstatic #2 // Field i:I
7: iconst_1
8: iadd
9: putstatic #2 // Field i:I
12: aload_1
13: monitorexit // synchronized关键字的出口
14: goto 22
17: astore_2
18: aload_1
19: monitorexit // synchronized关键字的出口
20: aload_2
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
17 20 17 any
}
从字节码指令中可以看到add方法的第3条指令处和第13、19条指令处分别有monitorenter和moniterexit两条指令。另外第4、7、8、9、13这几条指令其实就是i++的指令。由此可以得出在字节码中会在同步代码块的入口和出口加上monitorenter和moniterexit指令。当执行到monitorenter指令时,线程就会去尝试获取该对象对应的Monitor的所有权,即尝试获得该对象的锁。
当该对象的 monitor 的计数器count为0时,那线程可以成功取得 monitor,并将计数器值设置为 1,取锁成功。如果当前线程已经拥有该对象monitor的持有权,那它可以重入这个 monitor ,计数器的值也会加 1。而当执行monitorexit指令时,锁的计数器会减1。
倘若其他线程已经拥有monitor 的所有权,那么当前线程获取锁失败将被阻塞并进入到_EntryList中,直到等待的锁被释放为止。也就是说,当所有相应的monitorexit指令都被执行,计数器的值减为0,执行线程将释放 monitor(锁),其他线程才有机会持有 monitor 。
2.2、同步方法的实现
同步方法的字节码指令与同步代码块的字节指令有所差异。我们先来通过javap -v查看下面代码的字节码指令。
public synchronized void add(){
i++;
}
反汇编后可得到如下的字节指令
public synchronized void add();
descriptor: ()V
flags: (0x0021) ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #2 // Field i:I
5: iconst_1
6: iadd
7: putfield #2 // Field i:I
10: return
LineNumberTable:
line 5: 0
line 6: 10
可以看到这里并没有monitorenter和moniterexit两条指令,而是在方法的flag上加入了ACC_SYNCHRONIZED的标记位。这其实也容易理解,因为整个方法都是同步代码,因此就不需要标记同步代码的入口和出口了。当线程线程执行到这个方法时会判断是否有这个ACC_SYNCHRONIZED标志,如果有的话则会尝试获取monitor对象锁。执行步骤与同步代码块一致,这里就不再赘述了。