现在,如果要使用 Java 实现一段线程安全的代码,大致有 synchronized 、 java.util.concurrent 包等手段。虽然大家都会用,但却不一定真正清楚其在 JVM 层面上的实现原理,因此,笔者在查阅了一些资料后,希望把自己对此的一些见解分享给大家。
synchronized 关键字在不同的使用场景下,作为锁的对象有所不同,主要分为以下三种情况:
对于同步代码块,锁就是声明 synchronized 同步块时指定的对象(synchronized 括号中配置的对象);
对于普通对象方法,锁就是当前的实例对象;
对于静态同步块,锁就是当前类的 Class 对象。
我们可以通过一段代码来进一步说明 synchronized 是如何实现互斥同步的。
示例代码
public class SynchronizedTest {
public void test() {
synchronized (this) {
try {
System.out.println("SynchronizedTest.test() method start!");
} catch (Exception e) {
}
}
}
}
Compiled from "SynchronizedTest.java"
public class com.xxx.JVMTest.SynchronizedTest {
public com.xxx.JVMTest.SynchronizedTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String SynchronizedTest.test() method start!
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
20: aload_2
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
17 20 17 any
}
public class SynchronizedTest {
public synchronized void testTwo() {
System.out.println("SynchronizedTest.testTwo() method start!");
}
}
public synchronized void testTwo();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #6 // String SynchronizedTest.testTwo() method start!
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 23: 0
line 24: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this Lcom/xxx/JVMTest/SynchronizedTest;
public class AtomicInteger extends Number implements java.io.Serializable {
static {
try {
//获取 value 变量的偏移量, 赋值给 valueOffset
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
...other methods...
}
/*==========================================*/
public final class Unsafe {
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
//通过对象和偏移量获取变量的值
//由于 volatile 的修饰, 因此所有线程看到的 var5 都是一样的
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
...other methods...
}
可重入代码(纯代码)
是一种允许多个进程同时访问的代码。程序在运行过程中可以被打断,并由开始处再次执行,并且在合理的范围内(多次重入,而不造成堆栈溢出等其他问题),程序可以在被打断处继续执行,且执行结果不受影响。(可重入代码 | 百度百科)
可重入代码拥有一些共同的特征:
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/qq_43842093/article/details/122993240
内容来源于网络,如有侵权,请联系作者删除!