2014-02-18 2 views
1

От Java Параллелизма на практике:Что означает «потокобезопасность»?

package net.jcip.examples; 

import java.util.concurrent.atomic.*; 

/** 
* NumberRange 
* <p/> 
* Number range class that does not sufficiently protect its invariants 
* 
* @author Brian Goetz and Tim Peierls 
*/ 


public class NumberRange { 

    // INVARIANT: lower <= upper 
    private final AtomicInteger lower = new AtomicInteger(0); 
    private final AtomicInteger upper = new AtomicInteger(0); 

    public void setLower(int i) { 
     // Warning -- unsafe check-then-act 
     if (i > upper.get()) 
      throw new IllegalArgumentException("can't set lower to " + i + " > upper"); 
     lower.set(i); 
    } 

    public void setUpper(int i) { 
     // Warning -- unsafe check-then-act 
     if (i < lower.get()) 
      throw new IllegalArgumentException("can't set upper to " + i + " < lower"); 
     upper.set(i); 
    } 

    public boolean isInRange(int i) { 
     return (i >= lower.get() && i <= upper.get()); 
    } 
} 

Он говорит: «Оба setLower и setUpper являются регистрацией тогдашнего акта последовательности, но они не используют достаточный замок, чтобы сделать их атомарными. Если диапазон номеров удерживается (0, 10) и один поток вызывает setLower(5), а другой поток вызывает setUpper(4), с некоторым неудачным временем оба пройдут проверки в сеттерах, и будут применены обе модификации. В результате диапазон в настоящее время удерживает (5, 4) недопустимое состояние. »

Как это могло произойти, если AtomicInteger s являются потокобезопасными, я пропустил некоторые моменты? И как это исправить?

+6

, вероятно, потому, что атомистические цепочки безопасны для себя, но вы смешиваете операции двух независимых атомных интеграторов. выбор из одного, а затем установка на другом. –

ответ

1

Участие AtomicInteger не имеет ничего общего с безопасностью потока вашего вопроса.

Вот проблема:

  1. если (я> upper.get)
  2. lower.set (я);

шаги 1 и 2 могут быть индивидуально атомарными, но вместе они образуют двухступенчатое, неатомное действие.

Вот что может случиться:

  1. если blammy проходит
  2. переключение контекста в другой поток.
  3. другой поток вызывает upper.set (q) такой, что q < i
  4. контекстный переключатель обратно в эту ветку.
  5. нижний установлен на i.

каждый отдельный шаг является атомным по своей природе, но набор шагов не является атомарным.

Java, решение для этого:

  1. синхронизированы (some_object_reference, возможно, это)
  2. {
  3. , если (я> upper.get)
  4. lower.set (я)
  5. }

Удостоверьтесь, что у нас одна и та же ссылка на объект, чтобы синхронизировать все настройки верхнее и нижнее значения.

0

Позволяет создать объект:

NumberRange nr= new NumberRange(); 

Автор А:

nr.setLower(-1); //A1 

резьбы Б:

nr.setLower(-3); //B1 
nr.setUpper(-2); //B2 

Выполнение заказа: В1, то А1 и В2 в то же самое время: Если нить B проходит проверку перед A (-3 < -2), а затем A передает ее проверку до того, как B задает значение (-1 < 0), этот код не будет вызывать никаких ошибок, потому что ваши методы не являются атомарными. Проверка является атомарной, и метод set тоже, но вместе у вас есть 2 атомарных шага, а не один.

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