Можно ли безопасно использовать общий ресурс в качестве блокировки для синхронизированного блока? - PullRequest
0 голосов
/ 09 июля 2010

Я часто нахожу себя с кодом вроде

private static final MyType sharedResource = MyType();
private static final Object lock = new Object();
...
synchronized(lock)
{
    //do stuff with sharedResource
}

Действительно ли это необходимо или можно использовать sharedResource в качестве самой блокировки, например

private static final MyType sharedResource = MyType();
...
synchronized(sharedResource)
{
    //do stuff with sharedResource
}

Примечание: синхронизированные блоки, показанные в примерах, будут жить внутри методов, выполняющих работу, и не являются самими методами или синхронизированными методами.

РЕДАКТИРОВАТЬ: В некоторых ответах был отмечен очень хороший момент: если мы имеем дело с несколькими общими ресурсами, то первый метод «Объект» гораздо безопаснее.

ПРЕДУПРЕЖДЕНИЕ Важным является тот факт, что sharedResource равен static! Если это static, то синхронизированные методы или блокировка синхронизированных блоков на this не будут работать. Объект блокировки также должен быть static.

Ответы [ 5 ]

1 голос
/ 09 июля 2010

Плюсы и минусы обоих

Первый вариант плюсы: позволяет блокировать концепцию, а не объекты.Если бы вам пришлось заблокировать несколько ресурсов для одного действия (которое обычно не рекомендуется, но иногда необходимо), вы могли бы сделать это с гораздо меньшим беспокойством о состоянии гонки.

cons: объект все еще можно изменить,поэтому вам нужно убедиться, что доступ к объекту ограничен методами, которые соблюдают внешнюю блокировку.

Второй вариант плюсы: блокировка на объекте должна помешать другим изменять его (хотя вы должны дважды проверить точную симантику.) РЕДАКТИРОВАТЬ: имеет тот же кон, что и выше - если методы не synchronized внутренне, вы все равно можете столкнуться с методами, которые не соблюдают контракт.

cons: Вы блокируете доступ ко всем методамдаже те, которые не связаны с тем, что вы пытаетесь использовать, что может привести к замедлению и, возможно, к тупику.Однако вы могли бы легко доказать, что в этом случае ваш объект выполняет слишком много работы и должен быть разбит.

РЕДАКТИРОВАТЬ: Позвольте мне пояснить часть 2 здесь (случай разбить MyType наMyFoo и MyBar открыты для обсуждения ...)

class MyType {
   Foo foo;
   Bar bar;

   void doFoo() { foo.do(); }
   void doBar() { bar.do(); }
}

class MyActions {
    MyType thing;

    void lotsOfFoo() {
        // blocks bar :-(
        synchronized(thing) { thing.doFoo(); } 
    }

    void lotsOfBar() {
        // blocks foo :-(
        synchronized(thing) { thing.doBar(); } 
    }

}

Лично я использую вариант 1 гораздо чаще (поэтому я не уверен насчет этой части в варианте 2).

1 голос
/ 09 июля 2010

Единственная проблема, которую я вижу, с использованием sharedResource в качестве блокировки, если MyType имеет методы, определенные как synchronized. В этом случае происходит странное поведение, которое не было задумано разработчиками MyType. (См. Комментарий к кодировке свечения).

В противном случае все будет в порядке, однако если вам все равно нужна только одна блокировка, просто синхронизируйте на this вместо введения дополнительного фиктивного объекта.

Кстати, вы намеренно сделали объект lock статичным? Потому что это означает, что все экземпляры блокируются на одном и том же мониторе, то есть они могут блокировать друг друга, что может не соответствовать предполагаемому вами поведению.

0 голосов
/ 09 июля 2010

Лично я обычно не синхронизируюсь на произвольной блокировке.То есть я обычно делал бы что-то вроде вашего второго подхода:

private static final MyType sharedResource = MyType();
...
synchronized(sharedResource) {
    //do stuff with sharedResource
}

Конечно, перед запуском синхронизированного кода должна быть получена блокировка для целевого объекта.Часто я делаю блокировку «вниз» на шаг дальше, так сказать.Это значит, что я синхронизирую метод внутри sharedResource.Как в:

public class MyType {

    public void synchronized doWork() {

    }
}

Но когда дело доходит до такого рода вещей, сложно делать обобщения, потому что есть исключения из каждого правила.В конце концов, ваши общие требования к архитектуре будут определять, где должна происходить блокировка.Для меня чаще всего я обнаруживаю, что синхронизирую метод верхнего уровня, который получает доступ к общим ресурсам, и, таким образом, гораздо реже блокировать объект, который не делает ничего, кроме как выполняет функцию блокировки.грамматические исправления

0 голосов
/ 09 июля 2010

Если я правильно помню, синхронизированный - это монитор, он гарантирует, что только один поток может получить доступ к этому объекту в любой момент времени в одной и той же JVM. Поэтому я думаю, что вы обращаетесь только к shardResouce, достаточно синхронизации на shardResouce.

0 голосов
/ 09 июля 2010

Да, вы можете сделать это абсолютно.

На самом деле это улучшает четкость и уменьшает беспорядок.

...