2010-08-11 2 views
3

Прямо сейчас я пытаюсь узнать больше о java threading, и у меня есть небольшой вопрос, что я не могу найти прямой ответ нигде. Допустим, у меня есть два threadsthat как доля объекта:Вопрос о потоках и замках

public class FooA implements Runnable 
{ 
    Object data; 

    public FooA(final Object newData) 
    { 
     data = newData; 
    } 

    public void doSomething() 
    { 
     synchronized(data) 
     { 
      data = new Integer(1); 
     } 
    } 

    public void run() { 
     // Does stuff 
    } 
} 

public class FooB implements Runnable 
{ 
    Object data; 

    public FooB(final Object newData) 
    { 
     data = newData; 
    } 

    public void doSomething() 
    { 
     synchronized(data) 
     { 
      System.out.println(data); 
     } 
    } 
} 

Would FooA блок FooB, когда он находится в разделе DoSomething кода? Или наоборот? Чувство моего чувства говорит «да», но, согласно книге, которую я читаю, она говорит «нет». Отсюда необходимость в объектах мониторинга. Я сделал несколько более сложную версию этого, и все сработало нормально.

Я немного оглянулся, но не нашел конкретного ответа.

ответ

2

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

+0

Если данные были изменяемыми, они будут затем блокировать каждый раз? – UberJumper

+0

Если FooA указывает на другой объект, назначив новое значение элементу данных, FooB никогда не узнает и не узнает. Это не проблема как таковая. – djna

+0

@uberjumper - вы имеете в виду, если данные были неизменными *? Если у 'data' не был присвоен новый объект (т. Е. Само поле' data' никогда не мутируется), и оба объекта построены с тем же объектом, который будет назначен их полям данных, тогда да, оба будут правильно блокировать друг друга, когда они синхронизируются на 'data'. –

0

Это правильно, если вы должны синхронизировать один и тот же объект, но так как один поток изменяет объект, на который ссылаются «данные», вам нужно синхронизировать его с «этим».

+0

Только один из потоков, по-видимому, изменяет «данные» из исходного общего значения. –

1

Ответ да в этом случае, но он хрупкий (т. Е. Следующее изменение кода, вероятно, сломает что-то). Почему это работает?

Поскольку FooB никогда не замечает, что FooA изменяет объект (каждый поток получает свою собственную ссылку, так FooB Никогда не замечает, когда FooA присваивает его ссылку нового значения).

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

1

Любой объект Java может использоваться для синхронизации.

Если FooA и FooB построены со ссылками на один и тот же объект, то они делят один и тот же объект «блокировки» и будут блокироваться, как вы ожидаете. Как

Object data; 

decalrations не являются окончательными, либо FooA или FooB, или оба могут назначать различные значения для данных, а затем быть синхронизации на различных объектах - которые могут быть хорошо или плохо в зависимости от того, что вы пытаетесь сделать ,

1

В вашем примере кода fooA.data и fooB.data не тот же объект, если кто-то не инициализирует их таким образом что-то вроде:

Object o = new Object(); 
FooA fooA = new FooA(o); 
FooB fooB = new FooB(o); 

Если они не инициализируются к тому же, например, они не являются тем же объектом, только тот же тип и имя.

Когда FooA присваивает new Integer(1) до data, они снова не будут одним и тем же типом и именем. Таким образом, после того, что они не будут синхронизированы, если вы звоните:

fooB.data = fooA.data; 

Это должно произойти внутри блока синхронизации гарантировать синхронное выполнение.

Кроме того, что-то, что нужно знать о потоковой передаче, заключается в том, что даже если все работает один раз, это не означает, что ваша программа верна или что она будет работать каждый раз. Проблемы с потоками возникают только тогда, когда время срабатывает правильно (или просто неправильно).

3

В этом примере есть несколько проблем.

Во-первых, synchronized(data) означает, что он синхронизируется с объектом, находящимся в data в то время. Если вы инициализировали два объекта с одним и тем же объектом, вы должны получить синхронизацию.

Однако, поскольку вы сами устанавливаете data внутри кода, это не будет работать после этого (так как это будет не тот же объект).

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

Я сделал несколько более сложную версию , и все сработало нормально.

Очень сложно или почти невозможно отладить проблемы параллелизма путем проб и ошибок. Тот факт, что он не подведет, не означает, что он будет работать надежно.

Я рекомендую прочитать эту книгу: http://www.javaconcurrencyinpractice.com/

+0

+1 для «Очень сложно или почти невозможно отладить проблемы параллелизма путем проб и ошибок». Кроме того, это хуже, чем это; даже обычные методы fuzzing не обязательно будут поднимать проблемы параллелизма. (Прокручивая его в производство и делая громкую демонстрацию, хотя ...) –