Java 中关键字 synchronized 表示只有一个线程可以获取作用对象的锁,执行代码,阻塞其他线程。
作用:
- 确保线程互斥地访问同步代码
- 保证共享变量的修改能够及时可见
- 有效解决重排序问题
用法:
- 修饰普通方法
- 修饰静态方法
- 指定对象,修饰代码块
特点:
- 阻塞未获取到锁、竞争同一个对象锁的线程
- 获取锁无法设置超时
- 无法实现公平锁
- 控制等待和唤醒需要结合加锁对象的 wait() 和 notify()、notifyAll()
- 锁的功能是 JVM 层面实现的
- 在加锁代码块执行完或者出现异常,自动释放锁
原理:
- 同步代码块是通过 monitorenter 和 monitorexit 指令获取线程的执行权
- 同步方法通过加 ACC_SYNCHRONIZED 标识实现线程的执行权的控制
测试代码:
public class TestSynchronized { public void sync() { synchronized (this) { System.out.println("sync"); } } public synchronized void syncdo() { System.out.println("syncdo"); } public static synchronized void staticSyncdo() { System.out.println("staticSyncdo"); } }
通过JDK 反汇编指令 javap -c -v TestSynchronized
javap -c -v TestSynchronized Last modified 2019-5-27; size 719 bytes MD5 checksum e5058a43e76fe1cff6748d4eb1565658 Compiled from "TestSynchronized.java" public class constxiong.interview.TestSynchronized minor version: 0 major version: 49 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Class #2 // constxiong/interview/TestSynchronized #2 = Utf8 constxiong/interview/TestSynchronized #3 = Class #4 // java/lang/Object #4 = Utf8 java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Methodref #3.#9 // java/lang/Object."<init>":()V #9 = NameAndType #5:#6 // "<init>":()V #10 = Utf8 LineNumberTable #11 = Utf8 LocalVariableTable #12 = Utf8 this #13 = Utf8 Lconstxiong/interview/TestSynchronized; #14 = Utf8 sync #15 = Fieldref #16.#18 // java/lang/System.out:Ljava/io/PrintStream; #16 = Class #17 // java/lang/System #17 = Utf8 java/lang/System #18 = NameAndType #19:#20 // out:Ljava/io/PrintStream; #19 = Utf8 out #20 = Utf8 Ljava/io/PrintStream; #21 = String #14 // sync #22 = Methodref #23.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V #23 = Class #24 // java/io/PrintStream #24 = Utf8 java/io/PrintStream #25 = NameAndType #26:#27 // println:(Ljava/lang/String;)V #26 = Utf8 println #27 = Utf8 (Ljava/lang/String;)V #28 = Utf8 syncdo #29 = String #28 // syncdo #30 = Utf8 staticSyncdo #31 = String #30 // staticSyncdo #32 = Utf8 SourceFile #33 = Utf8 TestSynchronized.java { public constxiong.interview.TestSynchronized(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #8 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lconstxiong/interview/TestSynchronized; public void sync(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 7: ldc #21 // String sync 9: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 12: aload_1 13: monitorexit 14: goto 20 17: aload_1 18: monitorexit 19: athrow 20: return Exception table: from to target type 4 14 17 any 17 19 17 any LineNumberTable: line 6: 0 line 7: 4 line 6: 12 line 9: 20 LocalVariableTable: Start Length Slot Name Signature 0 21 0 this Lconstxiong/interview/TestSynchronized; public synchronized void syncdo(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack=2, locals=1, args_size=1 0: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #29 // String syncdo 5: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 12: 0 line 13: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lconstxiong/interview/TestSynchronized; public static synchronized void staticSyncdo(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED Code: stack=2, locals=0, args_size=0 0: getstatic #15 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #31 // String staticSyncdo 5: invokevirtual #22 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 16: 0 line 17: 8 LocalVariableTable: Start Length Slot Name Signature } SourceFile: "TestSynchronized.java"