创建和使用String对象来锁定线程同步是一个好习惯吗?

时间:2014-09-16 07:44:36

标签: java string multithreading synchronization synchronized

抱歉我的英文

我没有使用任何字段进行锁定,因此我不应该考虑某些字段是否有值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);
            }
        }

    }

}

4 个答案:

答案 0 :(得分:6)

您不应将String用于锁定对象,尤其不应使用字符串文字

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

演示:

// 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!
}

最好是在&#34;外部&#34;上无法访问的私有对象上进行同步。如果您使用Object锁定,可以从&#34;外部&#34; (或通过方法返回),任何人都可以使用该对象作为您无法控制的锁,并可能导致内部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
}

答案 1 :(得分:1)

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

这里你最好的选择是锁上实际的马桶。

答案 2 :(得分:1)

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

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

synchronized (t) {
            goToToilet(t);
 }

syncrhonized在这段代码中,对括号内对象的任何使用都是在brakets范围内的线程排他性,因此这个对象变成了一个锁。

答案 3 :(得分:1)

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

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

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

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