垃圾收集器和String.intern()

时间:2014-11-10 07:42:00

标签: java string garbage-collection

如果我这样做

StringBuilder b = new StringBuilder();
b.append("foo").append("bar");
String s = b.toString().intern();
s = null;

"foobar"创建的StringBuilder字符串是否可用于垃圾收集器?

我怀疑是intern()方法,因为我正在使用基于字符串的ID同步一些应用程序的代码块。

这样的事情:

String id = getUserCurrentId() // this method generates IDs with StringBuider...
synchronized(id.intern()){
    // .....
}

它是一个桌面应用程序,带有一些线程,每个登录用户在每次登录过程中拥有一个生成的ID。

2 个答案:

答案 0 :(得分:3)

我认为它会起作用:

虽然even interned Strings can be garbage collected(因此您可能在JVM的生命周期内获得不同的用户ID实例),但当您在同步块内时,它们不会被垃圾收集(因为那时String是仍然在使用中),所以它应该足够稳定以满足您的目的。

即使你下次再获得另一个实例,也只能意味着无论如何锁是无争议的,所以没关系。

但我仍然不会这样做。

如何使用a ConcurrentHashMap to get named locks

答案 1 :(得分:2)

当你调用str.intern()时,你确实可以访问字符串的内部实例,即如果这个sting已经在缓存中,你就会得到对这个实例的引用。

当你说:

String s = .... /* anything */

您创建的变量包含对同一字符串的另一个引用。 因此当你说

s = null; 

您只需将null添加到该引用中即可。它不会影响对象本身。因此,如果GC决定将其删除,它将被GC删除。您仍然可以使用源对象(直接写入赋值运算符的源对象)进行同步。当然synchronized(id.intern())看起来不错。虽然我不知道你为什么要这样做。

BTW重新使用在程序中具有功能意义的对象进行同步是一种非常糟糕的模式。考虑以下场景。您正在使用id.intern()进行同步。这意味着,如果您的ID是例如foo,并且该程序的其他部分中的某人说

String s = 'foo'

他可以访问同一个对象,因为字符串文字是缓存的。 现在如果在程序的其他部分编写以下代码:

String s = 'foo';
.....
synchronized(s) {
    s.notify();
}

并且在你的部分代码中写道

synchronized(id.intern()) {
    id.wait();
}

可能你的wait()会退出!程序的这种意外行为很难调试。因此,更好的做法是使用特殊对象进行锁定。