Философия Java3
вернуться

Эккель Брюс

Шрифт:

al untimedO. // True -- блокировка доступна al timedO. // True -- блокировка доступна // Теперь создаем отдельную задачу для установления блокировки new ThreadO {

{ setDaemon(true), } public void run {

al lock lockO.

System.out printlnC'acquired");

}

} startO,

Thread yieldO, // Даем возможность 2-й задаче al untimedO; // False -- блокировка захвачена задачей al.timedO. // False -- блокировка захвачена задачей

}

} /* Output-tryLockO. true

tryLock(2, TimeUnit.SECONDS): true acquired

tryLockO false

tryLock(2, TimeUnit SECONDS)- false */// ~

Класс ReentrantLock делает возможной попытку получения блокировки с последующим отказом от нее. Таким образом, если кто-то уже захватил блокировку, вы можете отказаться от своих намерений (вместо того, чтобы дожидаться ее освобождения). В методе timed делается попытка установления блокировки, которая может завершиться неудачей через 2 секунды (обратите внимание на использование класса Java SE5 TimeUnit для определения единиц времени). В main отдельный объект Thread создается в виде безымянного класса и устанавливает блокировку, чтобы методы untimed и timed могли с чем-то конкурировать.

Атомарные операции и ключевое слово volatile

В дискуссиях, посвященных механизму потоков в Java, часто можно услышать такое утверждение: «Атомарные операции не требуют синхронизации». Атомарная операция — это операция, которую не может прервать планировщик потоков — если она начинается, то продолжается до завершения, без возможности переключения контекста (переключения выполнения на другой поток). Не полагайтесь на атомарность, она ненадежна и опасна — используйте ее вместо синхронизации только в том случае, если вы являетесь экспертом в области синхронизации или, по крайней мере, можете получить помощь от такого эксперта.

Атомарные операции, упоминаемые в таких дискуссиях, включают в себя «простые операции» с примитивными типами, за исключением long и double.

Чтение и запись примитивных переменных гарантированно выполняются как атомарные (неделимые) операции. С другой стороны, JVM разрешается выполнять чтение и запись 64-разрядных величин (long и double) в виде двух раздельных 32-разрядных операций, с ненулевой вероятностью переключения контекста в ходе чтения или записи. Для достижения атомарности (при простом присваивании и возврате значений) можно определить типы long и double с модификатором volatile (учтите, что до выхода Java SE5 ключевое слово volatile не всегда работало корректно). Некоторые реализации JVM могут предоставлять более сильные гарантии, но вы не должны полагаться на платформенно-специ-фические возможности.

В многопроцессорных системах (которые в наши дни представлены многоядерными процессорами, то есть несколькими процесорами на одном чипе) видимость (visibility) играет гораздо более важную роль, чем в однопроцессорных системах. Изменения, вносимые одной задачей, — даже атомарные в смысле невозможности прерывания — могут оставаться невидимыми для других задач (например, если изменения временно хранятся в локальном кэше процессора). Таким образом, разные задачи будут по-разному воспринимать состояние приложения. Механизм синхронизации обеспечивает распространение видимости изменений, вносимых одной задачей в многопроцессорной системе, по всему приложению. Без синхронизации невозможно заранее предсказать, когда именно изменения станут видимыми.

Ключевое слово volatile обеспечивает видимость в рамках приложения. Если поле объявлено как volatile, это означает, что сразу же после записи в поле изменение будет отражено во всех последующих операциях чтения. Утверждение истинно даже при участии локальных кэшей — поля volatile немедленно записываются в основную память, и дальнейшее чтение происходит из основной памяти.

Если слепо следовать концепции атомарности, можно заметить, что метод getValue в следующем примере вроде бы отвечает этому описанию:

//: concurrency/AtomicityTest.java

import java.util.concurrent.*;

public class AtomicityTest implements Runnable { private int i = 0; public int getValueO { return i; } private synchronized void evenIncrement О { i++, i++. } public void run { while(true)

evenlncrementO;

}

public static void mam(String[] args) {

ExecutorService exec = Executors newCachedThreadPoolО; AtomicityTest at = new AtomicityTest; exec execute(at). while(true) {

int val = at.getValueO; if(val % 2 != 0) {

System.out.println(val), System.exit(0);

} /* Output

191583767

*///:-

Однако программа находит нечетные значения и завершается. Хотя return i и является атомарной операцией, отсутствие synchronized позволит читать значение объекта, когда он находится в нестабильном промежуточном состоянии. Вдобавок переменная i не объявлена как volatile, а это приведет к проблемам с видимостью. Оба метода, getValue и evenlncrement, должны быть объявлены синхронизируемыми. Только эксперты в области параллельных вычислений могут пытаться применять оптимизацию в подобных случаях.

  • Читать дальше
  • 1
  • ...
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • ...

Private-Bookers - русскоязычная библиотека для чтения онлайн. Здесь удобно открывать книги с телефона и ПК, возвращаться к сохраненной странице и держать любимые произведения под рукой. Материалы добавляются пользователями; если считаете, что ваши права нарушены, воспользуйтесь формой обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • help@private-bookers.win