先看段代码
public class Test2 {
public synchronized void test() {
System.out.println("hello");
}
public void test3() {
synchronized (this) {
System.out.println("hello");
}
}
public static synchronized void test2() {
System.out.println("hello");
}
}
常用用法,修饰对象,方法,静态方法
看原理
使用javap -c xx.class > 1.txt
public synchronized void test();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
public void test3();
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 hello
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 static synchronized void test2();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
对应上面的代码,class字节码
可以看出本质monitorenter和monitorexit指令
同步方法其实是
flags: ACC_SYNCHRONIZED,需要反编译成汇编可以直观地看见底层实现。
实际上,锁的本质满足如下:
1、原子性:当一个线程操作共享变量的时候,其他线程不能操作。实际就是串行修改。
2、可见性:当一个线程修改一个变量的时候,其他的线程及时知道更新缓存变量(参考计算机原理CPU与内存或主存的交互设计)
3、(可选)可重入:当一个线程拿到某个锁后,当该线程在释放该锁之前,可以重复获取该锁,当然释放次数与获取次数相同,否则死锁
从上可看出synchronized 的用途
1. 对于同步方法,锁是当前实例对象。
2. 对于同步方法块,锁是Synchonized括号里的对象。
3. 对于静态同步方法,锁是当前对象的Class对象。类似 synchronized (this.class)
JDK8及以后的版本JDK对synchronized做了优化,对于写操作频繁的线程安全推荐使用,性能较强
synchronized是操作对象头的monitor实现的,所以当时用sleep和wait的时候,在sleep方法中没有释放monitor,所以锁未释放,而wait方法释放了monitor对象,释放了锁