【问题标题】:Is it a good practice to create and use String objects for locking in thread synchronization?创建和使用 String 对象来锁定线程同步是一种好习惯吗?
【发布时间】:2014-11-09 21:03:28
【问题描述】:

对不起我的英语

我不使用任何字段进行锁定,因为我不应该考虑某些字段是否可以具有值 null

我总是创建仅用于锁定线程同步的特殊字段。

例如:

public class Worker {

    private static final List<Toilet> TOILETS = Arrays.asList(
        new Toilet(1),
        new Toilet(2),
        // ...
        new Toilet(NUMBER_OF_FLOORS)
    );

    // here it is:
    private static final List<String> LOCK_TOILETS = Arrays.asList(
        "LOCK TOILET #1",
        "LOCK TOILET #2",
        // ...
        "LOCK TOILET #" + NUMBER_OF_FLOORS
    );

    private final int floorNumber;

    public void spendWorkingHours() {
        for (int i = 0; i < X; ++i) {
            doWork();
            snackSomething();
            String lockToilet = LOCK_TOILETS.get(floorNumber);
            Toilet theOnlyToiletOnTheFloor = TOILETS.get(floorNumber);
            synchronized (lockToilet) {
                goToToilet(theOnlyToiletOnTheFloor);
            }
        }

    }

}

【问题讨论】:

  • 你应该同步你正在使用的实际对象,在这个例子中是厕所。
  • @ChristofferHammarström 如果这个变量可以是 null 怎么办?
  • @Christoffer Hammarström 不,你不应该。它增加了额外的约束。想象一下程序的其他独立部分做 sme。这导致两个独立的部分会相互干扰
  • @talex “干扰”是什么意思?目标不是防止厕所物体出现竞争条件吗?这种互斥“干扰”将确保程序安全。
  • @UmNyobe 也许是的。但是想象一下,我锁在马桶上,但使用不同的对象。

标签: java string multithreading synchronization synchronized


【解决方案1】:

您不应该将Strings 用于锁定对象,尤其是字符串文字

字符串文字来自String 池,每个String 文字是相同的字符串是same 引用。这意味着如果 2 个不同的线程使用 2 个“不同”的字符串文字实际上是相同的,因此很容易发生死锁。

演示:

// Thread #1
String LOCK1 = "mylock";
synchronized (LOCK1) {
}

// Thread #2
String LOCK2 = "mylock";
synchronized (LOCK2) {
    // This is actually the SAME lock,
    // might cause deadlock between the 2 synchronized blocks!
    // Because LOCK1==LOCK2!
}

最好是在“外部”无法访问的私有对象上进行同步。如果您使用Object 锁定从“外部”可见(或由方法返回),那么任何人都可以使用该对象作为您无法控制的锁,并可能导致您的内部死锁synchronized 块。
例如,您可以在您希望保护的对象上进行同步,如果它是私有的,或者创建一个私有的内部锁Object

private final Object LOCK = new Object();

// Later:
synchronized (LOCK) {
    // LOCK is not known to any "outsiders", safe to use it as internal lock
}

【讨论】:

  • 如果我们锁定私有且最终的字符串会怎样。私人最终字符串锁=“某值”;除了锁定目的之外,该字符串不会在任何地方使用。我们应该这样做吗?
  • @beinghuman 没有什么不同。如果您使用字符串文字来初始化它,并且如果其他人也使用相同的字符串文字,那么它仍然是相同的引用。语言规范涵盖/保证了这一点。
  • 你的回答很混乱。请说清楚一点。
【解决方案2】:

使用String 可能不是最好的主意,因为这个类得到了一些特殊处理,并且具有相同内容的字符串可能会被重复使用(因此锁定一楼的厕所也会锁定厕所)其他楼层相同)。

您最好的选择是锁定实际的马桶。

【讨论】:

    【解决方案3】:

    不需要lockToilet,为什么不在每个TOILET 资源上使用synchronized statement

    Toilet t;
    syncrhonized(TOILETS)
    {
         t = = TOILETS.get(floorNumber);
    }
    
    synchronized (t) {
                goToToilet(t);
     }
    

    syncrhonized 在这段代码中意味着括号之间的对象的任何使用在括号之间的范围内都是线程独占的,因此该对象成为锁。

    【讨论】:

      【解决方案4】:

      答案确实涵盖了您关于使用字符串进行锁定的问题(有关更多详细信息,请参阅字符串实习),因此我将仅提及其他一些注意事项:

      虽然您已将List 定义为final(无法分配另一个列表实例)并使用.asList(..)(无法更改大小)进行初始化,但这不会设为只读或thread-safe,所以如果有人更改该列表中的元素可能会进入不稳定状态。考虑使用只读列表。

      您还需要明确锁定的范围。你想锁定什么?如果goToToilet 更改了对象属性,那么同步点最好放在更改Object 状态的方法中。 (这是一个设计建议;代码可以工作,但将来更改代码时也容易出错)

      最后,我还要看看 java concurrent 结构,因为您可能会发现并发集合和锁定机制很有用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-06
        • 2014-04-01
        • 2016-04-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多