2016-09-26 5 views
0

Выписки из книги которого я читальныйСинхронизировать на тот же замок

// Синхронизировать на ложном замке, следовательно @NotThreadSafe

public class ListHelper<E> { 

    public List<E> list = Collections.synchronizedList(new ArrayList<E>()); 

    public synchronized boolean putIfAbsent(E x){ 
     boolean absent = !list.contains(x); 
     if(absent) 
      list.add(x); 
     return absent; 
    } 
} 

// Synchromize на тот же/правильном замок, следовательно @ThreadSafe
// Реализация Put-if-absent с блокировкой на стороне клиента.

public class ListHelper<E> { 

    public List<E> list = Collections.synchronizedList(new ArrayList<E>()); 

    public boolean putIfAbsent(E x){ 
     synchronized(list){ 
      boolean absent = !list.contains(x); 
      if(absent) 
       list.add(x); 
      return absent; 
     } 
    } 
} 

Двигаясь быстро на, автор переходит к 3-прецедентного
// Реализация пут-если отсутствовавший с использованием композиции.

public class ImprovedList<E> implements List<E> { 

    private final List<E> list; 

    public ImprovedList(List<E> list){ 
     this.list = list; 
    } 

    public synchronized boolean putIfAbsent(E x){ 

     boolean contains = list.contains(x); 
     if(contains) 
      list.add(x); 
     return !contains; 

    } 
} 

Как выше класс @ThreadSafe, даже когда list в public final List<E> list; мощь not be @ThreadSafe

+0

Тот список, будучи публично, не является потокобезопасным. Что это за книга? – markspace

+0

Joshua bloch JCIP –

+0

Неправильно вызывать любой из вышеперечисленных классов «потокобезопасный», когда «list' member« public ». –

ответ

1

Я считаю, что этот пример из JCIP. Учитывая это,

В случае 3 - он по-прежнему защищен резьбой, поскольку вы приобретаете замок на объекте ImprovedList в силу public synchronized boolean putIfAbsent(E x). Ключевым моментом здесь является , но все другие методы этого класса, которые работают с list, используют synchronized ключевое слово. Таким образом, только один поток сможет использовать этот объект list в любой момент времени.

Было бы более ясным, если мы попытаемся добавить другие методы, связанные с списком, и посмотреть, почему они работают.

public class ImprovedList<E> implements List<E> { 

    public final List<E> list; 

    public ImprovedList(List<E> list){ 
     this.list = list; 
    } 

    public synchronized boolean putIfAbsent(E x){ 

     boolean contains = list.contains(x); 
     if(contains) 
      list.add(x); 
     return !contains; 
    } 

    public synchronized void add(E x){ 
     list.add(x); 
    } 
    //...Other list methods, but using synchronized keyword as above method. 
} 

В случае 1 - Хотя public synchronized boolean putIfAbsent(E x) это не поточно, потому что ключевая вещь, чтобы отметить здесь другие методы без synchronized ключевого слова в ListHelper классе может обновлять list. ** Эти методы делают это, потому что считают, что это безопасно из-за Collections.synchronizedList(new ArrayList<E>()); **.

Было бы более ясным, если мы попытаемся добавить другие методы, связанные с списком, и посмотреть, почему это не удается.

public class ListHelper<E> { 

    public List<E> list = Collections.synchronizedList(new ArrayList<E>()); 

    public synchronized boolean putIfAbsent(E x){ 
     boolean absent = !list.contains(x); 
     if(absent) 
      list.add(x); 
     return absent; 
    } 

    //Not synchronized as this method assumes it is not necessary because the list itself is synchronized but doesn't help as above method and this method lock on different objects. 
    public void add(E x){ 
     list.add(x); 
    } 
    //...Other list methods but no synchronized keyword as above 
} 

Так в основном резюме заключается в использовании либо же замок, что и список (случай-2) или использует явный замок (случай -3).

Что касается @Markspace вопроса:

Этот список, будучи публично, не поточно. Что это за книга?

Это верно, и в этой мере была добавлена ​​осторожность. Он говорит,

Как Collections.synchronizedList и другие коллекции оберток, ImprovedList предполагает, что когда-то список передается в конструктор, клиент не будет использовать основной список непосредственно снова, доступ к нему только через ImprovedList.

+0

Can вы скажете мне, почему во втором случае существует «synchronized (list) {...}», даже если сам список синхронизирован. 'public list list = Collections.synchronizedList (новый ArrayList ()); ' –

+0

@ShirgillFarhanAnsari «Синхронизированный (список)» необходим для преодоления проблемы, которую мы имеем в случае «case-1», т. Е. Для получения блокировок в разных объектах и ​​для того, чтобы сделать внутри нее две отдельные операции atom '(list.contains & add)'. Помните в 'case-1', один метод' putIfAbsent' приобретает блокировку в 'ListHelper', где другие методы, такие как' add', 'clear' и т. Д., Блокируют объект' list'. Итак, в 'case-2', наконец, у нас есть все методы, получающие блокировку на одном и том же объекте и, следовательно,' list'. Ничего, кроме как в вашем комментарии говорится: // Синхронизируйте тот же/правильный замок, следовательно @ ThreadSafe' –

1

Можете ли вы сказать мне, почему во втором случае, синхронизируется (список) {...}, даже если список сам синхронизируется.

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

Что делать, если вы вытащили инструкцию synchronized, а затем вы позволили двум потокам вызвать ваш метод listHelper.putIfAbsent(x) с тем же x в то же время? Оба потока могут одновременно звонить list.contains(x), и оба могут установить значение absent в значение true. Как только они доберутся до этого момента, они оба вызовут list.add(x), а в потокобезопасном списке будут две ссылки на один и тот же объект.

Это похоже на то, что вы хотите.

Защита резьбы для list означает, что эти два потока не могут нанести вреда внутренней структуре списка, когда оба одновременно звонят list.add(x). Это означает, что list выполнит свои обещания, даже если он используется несколькими потоками одновременно.

Метод list.contains(x) соответствует его обещанию: он возвратил false оба раза, потому что x не было в списке. list.add(x) оправдал свое обещание, он поставил ссылку x в список как раз, так и было сказано.

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

Если вы не хотите список, чтобы содержать более одного x ссылку, то вы должны написать код, который предотвращает его от происходящего. Вот что делает оператор synchronized. Это приводит к тому, что атомная операция не проверяется, находится ли в списке x, а затем что-то делает. «Atomic» в основном означает, что два потока не могут сделать это одновременно.


Класс во втором случае является поточно, как указано в JCIP

Фраза «поточно» не имеет точного значения. Я не сказал, что ListHelper is не потолок безопасный; Я сказал, что было бы неправильно позвонить это «потокобезопасный».

Я просто придерживаюсь более консервативной позиции, чем г-н Блох, вот и все.

Класс является поточно если вы используете его как автор имел в виду для вас, чтобы использовать его. Но, сделав list участник public, он также очень упростил вам использование своего класса в безопасных для пользователя потоках.

Я пишу программное обеспечение для жизни, и в моей работе мне не нужно и не хватает программ . Моя задача - сделать программы, которые делают клиентов счастливыми. Если есть неправильный способ использовать мои программы, и если я сделаю неправильный путь более привлекательным, чем правильный, то клиенты не будут счастливы, мои боссы не будут счастливы, и оправдание: «Да , но это работает «Не собираюсь вытаскивать меня с крючка.

+0

Спасибо. Но когда вы говорите - ** Оба потока могли одновременно вызвать list.contains (x) .. ** Поскольку сам список синхронизирован 'list = Collections.synchronizedList (новый ArrayList ())', оба потока никогда не смогут вызов содержит метод одновременно. –

+0

@ShirgillFarhanAnsari, они могут _call_ это, но первое, что произойдет, в том, что один поток будет заблокирован, пока другой не будет закончен. –

1

Короткий ответ. Вам нужно прочитать другую книгу. Это сбивает с толку и неверно.

// Синхронизировать на ложном замке, следовательно @NotThreadSafe

Я предполагаю, что причина, почему эта книга говорит, что это synchronizing на ложном объекте (не синхронизировать на «замок») является то, что list является полем public. Это очень плохая практика. public Поля всегда опасны - особенно при использовании многопоточных приложений.

Если это «неправильная блокировка», поскольку она блокирует helper вместо list, то книга неверна. Вы можете сказать, что лучше использовать блокировку на list, но если это так, то это должно быть final.

// Синхронизировать на том же/правильный замок, поэтому @ThreadSafe

// Реализация Пут-если отсутствовавший с клиента на стороне Locking.

Но поле все еще public что означает, что какой-либо другой абонент может делать операции на list в то же время putIfAbsent(...) вызов делалась. Синхронизация в списке вместо помощника не имеет большого значения, если вы не можете каким-то образом гарантировать, что все остальные тоже это сделают. Это очень опасное предположение. Если true, то нет необходимости в обертке Collections.synchronizedList(...).

Например, другой вызов может позвонить listHelper.list.add(x)после того, какlist.contains(x) заканчивает вызов внутри от putIfAbsent(...) метода, который будет означать, что x бы в списке дважды. Нехорошо.

// Реализация put-if-absent с использованием композиции.

Это не thread-safe либо потому, что List передается в и нет никаких гарантий того, что абонент не продолжающие работать в списке неправильно. Вы можете сделать это потокобезопасным по копия все записи из списка, переданного во внутренний список. Этот список должен принадлежать помощнику, чтобы он был полностью потокобезопасным.

Как выше класс @ThreadSafe, даже если список в публичном финальном списке списка; не может быть @ThreadSafe

Это не так.Ни один из трех примеров не является потокобезопасным.

public class ListHelper<E> { 
    private final List<E> list = new ArrayList<E>(); 
    public boolean putIfAbsent(E x) { 
     synchronized (list) { 
      boolean absent = !list.contains(x); 
      if (absent) { 
       list.add(x); 
      } 
      return absent; 
     } 
    } 
} 

Этот класс, является потокобезопасной, потому что он владеет list, то list поле не public, а две операции на list являются synchronized. Он также был бы потокобезопасным, если ключевое слово synchronized было на методе putIfAbsent(...), а не list.

0

Я также вижу это в одной книге, см мой код, как показано ниже

public class ListHelper { 

public List<String> list = Collections.synchronizedList(new ArrayList<String>()); 

    public synchronized boolean putIfAbsent(String x) throws InterruptedException{// lock is ListHelper 
     boolean absent = !list.contains(x); 
     Thread.sleep(1000); 
     if(absent) 
      list.add(x); 
     return absent; 
    } 

    public void addList(String str) throws InterruptedException{ 
     Thread.sleep(500); 
     list.add(str); // lock is SynchronizedCollection mutex 
    } 

public static void main(String[] args) throws InterruptedException { 
    ListHelper lh = new ListHelper(); 
    MyThread t1 = new MyThread(lh); 
    MyThread2 t2 = new MyThread2(lh); 
    t1.start(); 
    t2.start(); 
    Thread.sleep(1500); 
    System.out.println("size="+lh.list.size()); 
    for(String str : lh.list){ 
     System.out.println("item="+str); 
    } 

} 

}

class MyThread extends Thread{ 
ListHelper lh; 
public MyThread(ListHelper lh){ 
    this.lh=lh; 
} 

@Override 
public void run() { 
    try { 
     lh.putIfAbsent("maksim"); 
    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 

}

class MyThread2 extends Thread{ 
ListHelper lh; 
public MyThread2(ListHelper lh){ 
    this.lh=lh; 
} 
@Override 
public void run() { 
    try { 
     lh.addList("maksim"); 
    } catch (InterruptedException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

} 

}

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