使用synchronized锁住字符串
我们使用synchronized 可以锁住字符串:
public static void testSynchronized1(String str) {
synchronized (str) {
try {
Thread.sleep(3000);
System.out.println("接收到了:" + str);
} catch (Exception ignore) {}
}
}
public static void main(String[] args) {
new Thread(() -> testSynchronized1("字符串1")).start();
new Thread(() -> testSynchronized1("字符串2")).start();
new Thread(() -> testSynchronized1("字符串3")).start();
new Thread(() -> testSynchronized1("字符串2")).start();
new Thread(() -> testSynchronized1("字符串2")).start();
}
但这样有一个问题:直接写的字符串是在常量池里面的,若是new出来的,即使字面量一致,也不是同一对象:
public static void main(String[] args) {
new Thread(() -> testSynchronized1(new String("字符串1"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
new Thread(() -> testSynchronized1(new String("字符串3"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
}
解决该问题的方法为:使用字符串的intern方法,获取(不存在则添加)常量池中的引用
public static void testSynchronized1(String str) {
synchronized (str.intern()) {
try {
Thread.sleep(3000);
System.out.println("接收到了:" + str);
} catch (Exception ignore) {}
}
}
public static void main(String[] args) {
new Thread(() -> testSynchronized1(new String("字符串1"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
new Thread(() -> testSynchronized1(new String("字符串3"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
}
但又带来新的问题,可以看到,另一个方法也锁了该字符串的话,那它们竞争的是同一个锁,达不到锁粒度仅为该方法的目的:
public static void testSynchronized1(String str) {
synchronized (str.intern()) {
try {
Thread.sleep(3000);
System.out.println("接收到了:" + str);
} catch (Exception ignore) {}
}
}
public static void testSynchronized2(String str) {
synchronized (str.intern()) {
try {
Thread.sleep(5000);
System.out.println("方法2接收到了:" + str);
} catch (Exception ignore) {}
}
}
public static void main(String[] args) {
new Thread(() -> testSynchronized1(new String("字符串1"))).start();
new Thread(() -> testSynchronized2(new String("字符串2"))).start();
new Thread(() -> testSynchronized1(new String("字符串3"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
}
我这边想到的一个解决思路为:给每个方法加一个唯一的字符串标识,再和参数组装为新的字符串:
public static void testSynchronized1(String str) {
String intern = new String(str + "testSynchronized1").intern();
synchronized (intern) {
try {
Thread.sleep(3000);
System.out.println("接收到了:" + str);
} catch (Exception ignore) {}
}
}
public static void testSynchronized2(String str) {
String intern = new String(str + "testSynchronized2").intern();
synchronized (intern) {
try {
Thread.sleep(5000);
System.out.println("方法2接收到了:" + str);
} catch (Exception ignore) {}
}
}
public static void main(String[] args) {
new Thread(() -> testSynchronized1(new String("字符串1"))).start();
new Thread(() -> testSynchronized2(new String("字符串2"))).start();
new Thread(() -> testSynchronized1(new String("字符串3"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
new Thread(() -> testSynchronized1(new String("字符串2"))).start();
}