2016-11-16 2 views
2

Я пытаюсь выяснить, что является лучшим способом использовать try/finally с замками.Java блокировка и разблокировка различными способами. Как попробовать/наконец?

Когда я lock() и unlock() в том же месте, я просто использовать try/finally блок как JavaDoc также предлагает:

lock.lock(); 
try{ 
    // do something 
} finally { 
    lock.unlock(); 
} 

Мне интересно, что это лучшая практика, чтобы использовать try/finally когда lock() и unlock() вызовы находятся в разных методах.

Для примера рассмотрим следующее:

public class X { 
    private ReentrantLock lock = new ReentrantLock(); 

    public void pickUp(){ 
    lock.lock(); 
    // do something 
    } 

    public void putDown(){ 
    // do something 
    lock.unlock(); 
    } 
} 

Что бы я сделать, это поставить try/finally блок на верхнем уровне, то есть всякий раз, когда я называю методы pickUp() и putDown(). Например, внутри run() метода:

// class code 
X x = new X(); 

public void run(){ 
    try{ 
    x.pickUp(); 
    // do something 
    } finally { 
    x.putDown(); 
    } 
} 

Является ли это правильный путь?

спасибо.

+1

Опасность этого заключается в том, что ваш '// делать что-то' в' putDown() 'может генерировать исключение, которое может помешать вызову' lock.unlock() '; поэтому вам нужно будет написать 'putDown()' таким образом, что 'lock.unlock()' определенно будет вызван (например, другой блок 'try/finally'). – khelwood

+0

Ну, но если '// делать что-то 'может вызвать исключение, я мог бы добавить предложение' catch' к тому же блоку 'try/finally', не так ли? –

ответ

1

Это действительно очень хороший кандидат для применения execute around method pattern.

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

Выполнение вокруг шаблона метода с этой загадкой, снова и снова инициализируя и очистив руки клиента. В основном вы вводите метод, который выполняет инициализацию и очистку ресурса должным образом, а между руками - до Consumer этого ресурса.

В идеале вы можете даже полностью инкапсулировать методы, которые выполняют инициализацию и очистку.

В качестве примера, на основании вашего примера кода:

private ReentrantLock lock = new ReentrantLock(); 

private void pickUp() { // now private 
    lock.lock(); 
    // do something 
} 

private void putDown() { // now private 
    // do something 
    lock.unlock(); 
} 

public void use(Consumer<X> consumer) { 
    try { 
    pickUp(); 
    consumer.accept(this); // client gets its hands on the properly initialized resource. 
    } finally { 
    putDown(); 
    } 
} 

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

+0

@MarkoTopolnik У меня будет больше времени, чтобы развернуть его – bowmore

+0

@MarkoTopolnik надеюсь, вам понравится сейчас! :) – bowmore

Смежные вопросы