Статьи

Пять способов обновления полей безопасным потоком: Часть 1

Существует пять способов обновления поля в Java потокобезопасным способом. Но прежде чем мы начнем, на что вы должны смотреть? Если вы получаете доступ к полю из множества потоков, вы должны убедиться, что:

  1. Изменения сделаны видимыми для всех тем
  2. Значение не изменяется во время обновления другими потоками
  3. Читающие потоки не видят противоречивое промежуточное состояние

Вы можете достичь этого одним из следующих 5 способов:

Волатильное поле

Когда использовать?

Вы можете использовать энергозависимое поле, когда у вас обновляется только один поток, а многие потоки читают однозначное поле. Или вы можете использовать его, когда записывающие потоки не читают поле. Вы можете использовать его только для однозначных полей, таких как boolean или int. Если вы хотите обновить графы объектов или коллекции, используйте copy on write, как описано ниже.

пример

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

public class WorkerThread extends Thread 
{ 
    private volatile boolean canceled = false; 
    public void cancelThread() 
    { 
        this.canceled = true; 
    } 
    @Override public void run() 
    { 
       while( ! canceled ) 
       { 
          // Do Some Work 
       } 
    } 
} 

Как это работает?

Объявление поля volatile делает изменения, сделанные одним потоком, видимыми для всех других потоков. Поскольку поток записи не читает значение, выполняется пункт b «значение не изменяется во время обновления другим потоком». Поскольку поле представляет собой одно значение, точка c «чтение потоков не видит противоречивое промежуточное состояние» также выполняется.

Как проверить?

Используя vmlens , плагин Eclipse для тестирования многопоточного программного обеспечения и для определения условий гонки Java, мы можем найти поля, которые должны быть объявлены как volatile. После объявления поля volatile мы можем проверить в представлении vmlens «порядок событий» , что поле правильно прочитано и записано:

Порядок событий в vmlens

Копирование при записи

Когда использовать?

Используйте copy on write, если вы хотите обновить граф объектов или коллекцию, а потоки в основном читаются и обновляются редко.

пример

Ниже показан метод добавления и получения из java.util.concurrent.CopyOnWriteArrayList:

private transient volatile Object[] array; 
final Object[] getArray() 
{   
  return array; 
} 
public boolean add(E e) 
{ 
   final ReentrantLock lock = this.lock; 
    lock.lock(); 
     try { 
          Object[] elements = getArray(); 
          int len = elements.length; 
          Object[] newElements = Arrays.copyOf(elements, len + 1); 
          newElements[len] = e; 
          setArray(newElements); return true; } 
     finally { 
          lock.unlock(); 
      } 
} 
public E get(int index) 
{ 
   return get(getArray(), index); 
} 

Как это работает?

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

Как проверить?

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

Точка ожидания в vmlens

Блокировка на основе атомарного обновления

Когда использовать?

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

пример

В следующем примере показан счетчик на основе блокировки:

public class LockBasedCounter {

private int i = 0;

public synchronized void addOne()
{
i++;
}


public synchronized int get()
{
return i;
}


}

Как это работает?

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

Как проверить?

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

Точка ожидания в vmlens

Заключение

Какой из первых 3 из 5 способов вы используете для обновления поля безопасным для потока способом, зависит от ваших требований к производительности и безопасности. Независимо от того, каким образом вы используете, вы должны проверить это. Узнайте больше о модульном тестировании многопоточного программного обеспечения с помощью vmlens и concurrent-junit, чтобы по- новому взглянуть на тестирование многопоточного Java-кода . Если у вас есть вопрос или замечание, пожалуйста, добавьте комментарий ниже и следите за новостями завтра!