动态生成Long类型数据不能作synchronized锁对象的解决方法(String中intern()方法的使用)

在前期系统开发中,系统升级成了区域版本的,一个系统拥有多个机构,机构ID字段是字符串类型的,在java中由于字符串可以缓存在常量池中,之前都是用机构ID作为锁条件的,这样可以保证,同一个机构是串行的而不同机构是并行的。

最近使用新框架,机构ID由String类型升级成了Long类型,这样再直接使用机构ID作synchronized的锁对象肯定是不行的,因为并没有常量池来缓存Long类型数据,不过跟Integer类型一样,Long类型自身也是做了缓存的:

public static Long valueOf(long l) {
        final int offset = 128;
        if (l >= -128 && l <= 127) { // will cache
            return LongCache.cache[(int)l + offset];
        }
        return new Long(l);
    }

可以看到缓存区域也是[-128,127],在这个区域内使用栈赋值的方式声明变量,可以保证引用地址是同一个,超过这个区域就不行了,我不能保证机构ID只在这个范围内啊,怎么解决呢?
首先想到的就是把机构ID重新转成字符串,不就行了吗。
Long.toString方法源码:

 public static String toString(long i) {
        if (i == Long.MIN_VALUE)
            return "-9223372036854775808";
        int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
        char[] buf = new char[size];
        getChars(i, size, buf);
        return new String(buf, true);
    }

可以看到,他每次返回的都是一个new出来的字符串对象,这样锁对象就变了,是锁不住的。这个时候String的intern方法就出场了。

在jdk1.6中,该方法把字符串的值复制到常量区,然后返回常量区里这个字符串的值;
在jdk1.7里,该方法在常量区记录该字符串首次出现的实例引用,然后返回该地址,常量区可以保存字面量也可以保存字符串对象在堆中的引用。
因为jdk7中intern方法是(在常量区找不到该字符串时)将该字符串对象在堆里的引用注册到常量区,以后使用相同字面量(双引号形式)声明的字符串对象都指向该地址,也就是该字符串在堆中的地址。用代码说明一下:

public static void main(String[] args) {
        String str1 = new String("abc");
        String str2 = (new String("a") + new String("bc"));
        System.out.println(str1 == str2);// false
        System.out.println(str1.intern() == str2.intern());// true
    }

上面的str1和str2地址肯定是不相同的,但是用于字面量是相同的所以,intern方法的返回值再比较就是相同的啦,这样问题就就解决了:

Long jiGouId = Loginutils.currentDoneOrgId();
String lockCondition = Long.toString(jiGouId).intern();
synchronized (lockCondition) {
	// dosomething
}