阿泽在学习synchronized关键字小结
首先呢总结一下synchronized的三种使用方式:
- 修饰实例⽅法,给对象实例加锁
synchronized void method(){
}
- 修饰静态⽅法,给当前类加锁
synchronized static void method(){
}
- 修饰代码块,给当前对象/类加锁
synchronized(this){
}
重点在于第三点,很多知识博客中都提醒了这样一点
尽量不要使⽤ synchronized(String a) 因为 JVM 中,字符串常量池具有缓存功能!
虽然大牛们已经给出了原因,但阿泽确实半肚子墨水,没明白缓存为何会影响加锁的问题?一鼓作气探个究竟!
情景再现:
package pers.zzk.jianzhi_offer;
public class DeadLockDemo {
private static String resource1="aaa";
private static String resource2="aaa";
public static void main(String[] args) {
new Thread(() -> {
synchronized (resource1) {
System.out.println(Thread.currentThread() + "get resource1");
try {
//保持resource1不释放
while(true) {
System.out.println(Thread.currentThread() + "get resource1");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程 1").start();
new Thread(() -> {
synchronized (resource2) {
System.out.println(Thread.currentThread() + "get resource2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "线程 2").start();
}
}
以上代码中我们创建了两个资源resource1和resource2,String类型且指向同一个对象。
线程1中我们占用resource1,并一直占用不释放;线程2中我们占用resource2
下面就是见证奇迹的时刻!
OMG!程序竟然只执行了线程1,而线程2 被阻塞了!!!,ok现在是不是有点明白缓存那味儿了。JVM识别String类型,加锁在同一个对象,必然会阻塞。
那么如何避免呢?
阿泽尝试了两种方法供大家参考
1、使用Object类型创建数据,避免缓存
private static Object resource1 = new Object();//资源 1
private static Object resource2 = new Object();//资源 2
2、使用new 字符创建一个新的对象
private static String resource2=new String("aaa");
我们来看一下任一种修改后的结果:
线程2成功执行!NICE!