[转]不要将锁加到可能被重用到的对象上(Do not synchronize on objects that may be reused)

滥用同步组件是造成并发问题的一个普遍性根源,在一个可能被重用的对象上加锁可能造成死锁或者其他不确定的影响。因此,程序不许永远不要在可能被重用的对象上加锁。

1. 不规范代码示例 (Boolean Lock Object)

private final Boolean initialized = Boolean.FALSE;
public void doSomething() {
  synchronized (initialized) {
    // ...
  }
}

Boolean 类型不适合用来加锁,因为它只有两个值:true, false. 包含相同值的布尔字段在 JVM 中共享布尔类的惟一实例。在上面的例子上,initialized 引用与值 Boolean.FALSE 对应的实例,如果其他的代码无意中使用相同的值 (Boolean.FALSE) 加锁在一个 Boolean 字段上,lock 实例将被重用,系统可能变得无响应或死锁。

2. 不规范代码示例 (包装类型 Boxed Primitive)

private int count = 0;
private final Integer Lock = count; // Boxed primitive Lock is shared
 
public void doSomething() {
  synchronized (Lock) {
    count++;
    // ...
  }
}

包装类型在一定的 integer 值范围内可能使用相同的实例 (Integer 的常量池是由 - 128 至 127 组成。当我们给一个 Integer 赋的值在这个范围之类时就直接会从缓存返回一个相同的引用;而超过这个范围时,就会重新 new 一个对象。);因此,他们遭受着与 Boolean 常量相同的重用问题。当值可以表示为字节时,包装对象被重用; JVM 还会在更大范围的值内重用该包装对象。当使用与包装类 Integer 相关的内在锁时(intrinsic lock) 是不安全的;当 new Integer 对象实例时使用 (new Integer(value)) 可以使该对象实例是惟一的且不会被重用。通常,任何包含已装箱值 (boxed value) 的数据类型上的锁都是不安全的.

2.1 兼容的解决方案 (Integer)

使用私有锁对象习惯语法的变体,使用 doSomething() 方法新建 Integer 实例用来加锁同步。

private int count = 0;
private final Integer Lock = new Integer(count); 
public void doSomething() {
  synchronized (Lock) {
    count++;
    // ...
  }
}

当显式构造整数对象时,它有一个惟一的引用和它自己的内部锁,它不仅与其他 Integer 对象不同,而且与具有相同值的 boxed Integer 也不同。虽然这是一个可接受的解决方案,但它可能会导致维护问题,因为开发人员可能错误地认为装箱整数 (boxed integers) 也是适当的锁对象。更合适的解决方案应如此规则的最终兼容解决方案中所述,对 private final lock object 进行同步。

3. 不规范代码示例 (字符串对象 Interned String Object)

private final String lock = new String("LOCK").intern(); 
public void doSomething() {
  synchronized (lock) {
    // ... 
  }
}

根据 Java API class java.lang.String 文档,当调用 intern() 方法时,如果池中已经包含了由 equals(object) 方法确定的与此字符串对象相等的字符串,则返回池中的字符串。否则,将此字符串对象添加到池中,并返回对该字符串对象的引用。

因此,一个 Interned 字符串对象的行为就像 JVM 中的一个全局变量。 正如在这个不规范代码示例中所演示的,即使对象的每个实例都维护自己的锁字段,这些字段也都引用一个相同字符串常量。使用 String 常量用于加锁与使用 Boolean 常量加锁具有相同的重用问题。

4. 不规范的代码 (字符串常量 String Literal)

// This bug was found in jetty-6.1.3 BoundedThreadPool
private final String lock = "LOCK";
public void doSomething() {
  synchronized (lock) {
    // ...
  }
}

String Literal 是常量,并且自动被 interned。因此,这个示例与前面的不兼容代码示例面临相同的陷阱。

4.1 兼容的解决方案 (String Literal)

private final String lock = new String("LOCK");
public void doSomething() {
  synchronized (lock) {
    // ...
  }
}

字符串实例与字符串 literal 不同。实例有一个惟一的引用和它自己的固有锁 (intrinsic lock),此示例与其他字符串对象实例或 literal 不同。不过,更好的方法是在 private final lock 对象上同步,如下面的兼容解决方案所示。

4.2 兼容的解决方案 (Private Final Lock Object)

private final Object lock = new Object();
public void doSomething() {
  synchronized (lock) {
    // ...
  }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,923评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,154评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,775评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,960评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,976评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,972评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,893评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,709评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,159评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,400评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,552评论 1 346
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,265评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,876评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,528评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,701评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,552评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,451评论 2 352

推荐阅读更多精彩内容

  • 这是16年5月份编辑的一份比较杂乱适合自己观看的学习记录文档,今天18年5月份再次想写文章,发现简书还为我保存起的...
    Jenaral阅读 2,746评论 2 9
  • 四、集合框架 1:String类:字符串(重点) (1)多个字符组成的一个序列,叫字符串。生活中很多数据的描述都采...
    佘大将军阅读 749评论 0 2
  • 九种基本数据类型的大小,以及他们的封装类。(1)九种基本数据类型和封装类 (2)自动装箱和自动拆箱 什么是自动装箱...
    关玮琳linSir阅读 1,883评论 0 47
  • 笑来老师说的前两单元意思:一是总是静不下来,因为你觉得是没时间,不会管理时间,这是一个问题,他说,你不能管理它,你...
    绽放精彩_萨萨阅读 99评论 0 0
  • 今天儿子又像往常一样不想起床,我的第一反应就是这孩子又开始耍赖了,是不是被我惯坏了。聊了几句,他说妈妈,学校的工作...
    爱我的宝贝儿子阅读 385评论 0 2