String作为同步锁测试

众所周知,synchronized 同步锁,是通过引用来锁的,那么通过锁字符串的时候,就可能有问题,就是不知道外常传的String字符串是否为同一个引用。例如,一个webController接收的相同字符串,他的引用地址就不一样。

那么可不可以通过 String.intern()的方法,获取常量池对象,使得对象地址相同呢。一般情况是可以的,但是有些情况也不行

例如当发生gc时,有时会有问题

String prefix = "hehe";
System.out.println(System.identityHashCode((prefix+x).intern()));
System.gc();

经测试,上面的情况,每次地址都不同。

通过查询可知,prefix + x  (非final变量)底层是自动new StringBuilder 拼接字符串在toString()的。如果是 “1”+“2”,或者final类型的String的拼接, 则jvm自动将其优化为“12”

但是不了解的是,如果 代码改成

System.out.println(System.identityHashCode(x.intern()));
System.gc();

虽然x的原始地址不同,x.intern()后地址相同,但是gc后,下次访问 x.intern()的地址还是相同,也就是说,常量池中的x并没有并回收。

不管是String,还是Integer,Long等情况,都存在值相同,引用不同的情况。为了解决这个问题,我们可以使用一个Map帮忙辅助试下

private static Map<Object, Object> lockMap = new ConcurrentHashMap<>(); 

public static void main(String[] args) throws InterruptedException {
    Object obj = null;
    Object lock = lockMap.computeIfAbsent(obj, k -> new Object());
    synchronized (lock) {
System.out.println("业务代码");
}
}

 延申:

JDK1.6 中,将这个字符串对象尝试放入串池。

(1) 如果串池中有,则并不会放入。返回已有的串池中的对象的地址
(2)如果没有,会把此对象复制一份,放入串池,并返回串池中的对象地址

JDK1.7 起,将这个字符串对象尝试放入串池。
(1)如果串池中有,则并不会放入。返回已有的串池中的对象的地址
(2)如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址

 

 

————————————————
该内容引用:https://blog.csdn.net/qq_45949008/article/details/119704398

posted @ 2022-09-18 10:26  IT新手村  阅读(51)  评论(0编辑  收藏  举报