-
synchronized基础
1、对于普通方法,锁是当前实例
2、对于静态同步方法,锁是当前类的class对象
3、对于同步代码块,锁是Synchronized是括号里的配置对象
-
synchronized可以修饰方法或者以同步块的形式来进行使用,主要保证多个线程在同一个时刻只有一个线程处于方法或者同步代码块中,保证了线程对变量访问的可见性和排他性。
public static void main(String[] args) {
synchronized (SynchronizedTest.class) {
System.out.println("hello world");
}
}
Javap -v XX.class 查看字节码文件
// 同步代码块
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: ldc #2 // class com/wayne/study/thread/threadpool/SynchronizedTest
2: dup
3: astore_1
4: monitorenter 监视器进入,获取锁
5: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #4 // String hello world
10: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit 监视器退出,释放锁
15: goto 23
18: astore_2
19: aload_1
20: monitorexit 监视器退出,释放锁
21: aload_2
22: athrow
23: return
// 同步方法 ACC_SYNCHRONIZED
public static synchronized void first();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=0, args_size=0
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #7 // String first
5: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 13: 0
line 14: 8
同步代码块采用的是monitorenter和monitorexit指令,同步方法采用的是ACC_SYNCHRONIZED修饰符。其本质就是获取对象的监视器(monitor),这个获取的过程是排他的,同一时刻只有一个线程能够获取synchorized所保护的对象监视器。
任何一个对象都有一个监视器,当这个对象由同步代码块或者这个对象的同步方法调用的时候,执行的线程必须先获取这个对象的监视器才能进入这个代码块或方法,如果没有获取到对象的监视器的线程就会被阻塞,进入block状态。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
-
锁
1、锁的状态分为:无锁、偏向锁、轻量级锁、重量级锁
2、锁的这几种状态,会随着竞争的情况慢慢升级,而且是不可逆的,不可降级的
3、当我们new一个对象出来的时候,它是无锁状态的,当竞争慢慢增加之后,锁才会慢慢升级
-
锁升级
1、偏向锁
大多数情况下,锁不存在多线程竞争,而且主要是由同一个线程多次获得,为了让获取锁的代价更低从而引入偏向锁。
当一个线程获取锁的时候,会在该对象的对象头和栈针中存储锁偏向的线程ID,下一次线程访问该对象不需要CAS操作来获取锁,只需要比较当前对象头的mark word是否指向该线程ID。
JVM参数-XX:UseBiasedLocking=false 关闭偏向锁,程序默认进入轻量级锁。
-XX:BiasedLockingStartupDelay=0关闭偏向锁启动延迟
如果没有线程竞争,非匿名偏向锁释放后会变成匿名偏向锁
2、轻量级锁
当对象处于非匿名偏向锁的状态下,当另外一个线程来竞争锁,并且竞争失败,该线程就会进入自旋。持有该对象的锁的线程执行到安全点的时候,偏向锁会膨胀为轻量级锁。
竞争的线程不会阻塞,会自旋来竞争锁,自旋会消耗cpu。【追求相应时间,同步块执行速度很快】
3、重量级锁
轻量级锁竞争的时候,线程不断自旋获取锁,获取失败的话,线程会被阻塞加入阻塞队列,轻量级锁膨胀为重量级锁。当持有重量级锁执行完同步代码块结束之后,会释放锁唤醒阻塞队列中的线程,并重新竞争锁。
需要阻塞和唤醒线程,上下文的切换等系统开销。【追求吞吐量,同步块执行较长】
-
Java对象头
synchronized用的锁是存放在对象头里面的
Object obj = new Object(); System.out.print(ClassLayout.parseInstance(obj).toPrintable()); System.out.println("------------------------------------------------"); Thread t1 = new Thread(() -> { synchronized (obj) { System.out.print(ClassLayout.parseInstance(obj).toPrintable()); System.out.println("-------------------------------------------"); } }); t1.start(); t1.join(); for (int i=0; i<2; i++) { Thread t2 = new Thread(() -> { synchronized (obj) { System.out.print(ClassLayout.parseInstance(obj).toPrintable()); System.out.println("------------------------------------------"); } }); t2.start(); } java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ---------------------------------------------------------------------------------- java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 70 f3 1b 1f (01110000 11110011 00011011 00011111) (521925488) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ---------------------------------------------------------------------------------- java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 8a 71 59 1b (10001010 01110001 01011001 00011011) (458846602) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ---------------------------------------------------------------------------------- java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 8a 71 59 1b (10001010 01110001 01011001 00011011) (458846602) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total ----------------------------------------------------------------------------------