synchronized加锁String踩坑日记
本文参考 https://juejin.im/post/59fffddc5188253d6816f9c1 JVM内存分布
我们知道String.intern()方法会判断该字符串是否存在常量池中,如果存在直接获取,不存在将当前字符串放到常量池中
-
String 分情况存在于常量池跟JVM堆中,首先让我们来看一下例子一:
public class TestStringSycn { public static void main(String[] args) { String a = "a"+"b"; System.out.println(a); } }
我们使用javap -c TestStringSycn.class来看编译器如何帮我们解析的:
1$ javap -c TestStringSycn.class 2Compiled from "TestStringSycn.java" 3public class TestStringSycn { 4 public TestStringSycn(); 5 Code: 6 0: aload_0 7 1: invokespecial #1 // Method java/lang/Object."<init>":()V 8 4: return 9 10 public static void main(java.lang.String[]); 11 Code: 12 0: ldc #2 // String ab 13 2: astore_1 14 3: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 15 6: aload_1 16 7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 17 10: return 18} 19 20 21
我们可以看到,编辑器直接是帮我们直接拼接在一起,也就是说这里其实会在常量池里面存在 ab 这个常量 2. 例子二:
1public class TestStringSycn { 2 public static void main(String[] args) { 3 String a = "key"; 4 String b = "zzz"; 5 String c = a+ b; 6 System.out.println(c); 7 } 8} 9$ javap -c TestStringSycn.class 10Compiled from "TestStringSycn.java" 11public TestStringSycn { 12 public TestStringSycn(); 13 Code: 14 0: aload_0 15 1: invokespecial #1 // Method java/lang/Object."<init>":()V 16 4: return 17 18 public static void main(java.lang.String[]); 19 Code: 20 0: ldc #2 // String key 21 2: astore_1 22 3: ldc #3 // String zzz 23 5: astore_2 24 6: new #4 // class java/lang/StringBuilder 25 9: dup 26 10: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V 27 13: aload_1 28 14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 29 17: aload_2 30 18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31 21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 32 24: astore_3 33 25: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 34 28: aload_3 35 29: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 36 32: return 37} 38 39 40
我们可以看到,这里采用的是StringBuilder直接帮我们拼接而成,最后调用toString()方法,所以每次都会在堆上面生成一个String对象。
回到主题
当我们使用synchronized加锁String的时候,我们需要保证当前加锁的key是唯一的,通过例子1,2知道。如果要加锁String,最好是加锁String.intern()方法。eg:
1public class TestStringSycn { 2 public static void main(String[] args) { 3 String type = "order"; 4 String id = "1"; 5 String key = type + id; 6 synchronized (key.intern()) { 7 //执行方法 8 } 9 } 10}
