Брюс Эккель - Философия Java3

Скачивание начинается... Если скачивание не началось автоматически, пожалуйста нажмите на эту ссылку.
Жалоба
Напишите нам, и мы в срочном порядке примем меры.
Описание книги "Философия Java3"
Описание и краткое содержание "Философия Java3" читать бесплатно онлайн.
Хотя try-finally требует большего объема кода, чем ключевое слово synchronized, явное использование объектов Lock обладает своими преимуществами. При возникновении проблем с ключевым словом synchronized происходит исключение, но вы не получите возможность выполнить завершающие действия, чтобы сохранить корректное состояние системы. При работе с объектами Lock можно сделать все необходимое в секции finally.
В общем случае использование synchronized уменьшает объем кода, а также радикально снижает вероятность ошибки со стороны программиста, поэтому явные операции с объектами Lock обычно выполняются только при решении особых задач. Например, с ключевым словом synchronized нельзя попытаться получить блокировку с неудачным исходом или попытаться получить блокировку в течение некоторого промежутка времени с последующим отказом — в подобных случаях приходится использовать библиотеку concurrent:
//: concurrency/AttemptLocking java
// Объекты Lock из библиотеки concurrent делают возможными
// попытки установить блокировку в течение некоторого времени
import java.util.concurrent *;
import java util concurrent.locks.*;
public class AttemptLocking {
private ReentrantLock lock = new ReentrantLockO;
public void untimedO {
boolean captured = lock.tryLockO, try {
System.out printlnCtryLockO: " + captured); } finally {
if(captured)
lock unlockO;
}
}
public void timedO {
boolean captured = false; try {
captured = lock tryLock(2, TimeUnit SECONDS); } catch(InterruptedException e) {
throw new RuntimeException(e);
}
try {
System out println("tryLock(2. TimeUnit SECONDS): " +
captured),
} finally {
if(captured)
lock unlockO,
}
}
public static void main(String[] args) {
final AttemptLocking al = new AttemptLocking(),
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(), должны быть объявлены синхронизируемыми. Только эксперты в области параллельных вычислений могут пытаться применять оптимизацию в подобных случаях.
В качестве второго примера рассмотрим кое-что еще более простое: класс, производящий серийные номера25. Каждый раз при вызове метода nextSerialNum-ber() он должен возвращать уникальное значение:
//: concurrency/SerialNumberGeneratorJava
public class SerialNumberGenerator {
private static volatile int serial Number = 0, public static int nextSerialNumberО {
return serialNumber++; // Операция не является потоково-безопасной
}
} ///.-
Представить себе класс тривиальнее SerialNumberGenerator вряд ли можно, и если вы ранее работали с языком С++ или имеете другие низкоуровневые навыки, то, видимо, ожидаете, что операция инкремента будет атомарной, так как инкремент обычно реализуется в виде одной инструкции микропроцессора. Однако в виртуальной машине Java инкремент не является атомарным и состоит из чтения и записи, соответственно, ниша для проблем с потоками найдется даже в такой простой программе.
Поле serialNumber объявлено как volatile потому, что каждый поток обладает локальным стеком и поддерживает в нем копии некоторых локальных переменных. Если вы объявляете переменную как volatile, то тем самым указываете компилятору не проводить оптимизацию, а это приведет к удалению чтения и записи, удерживающих поле в соответствии с локальными данными потока. Операции чтения и записи осуществляются непосредственно с памятью без кэширования. Кроме того, volatile не позволяет компилятору изменять порядок обращений с целью оптимизации. И все же присутствие volatile не влияет на тот факт, что инкремент не является атомарной операцией.
Для тестирования нам понадобится множество, которое не потребует переизбытка памяти в том случае, если обнаружение проблемы отнимет много времени. Приведенный далее класс CircularSet многократно использует память, в которой хранятся целые числа (int); предполагается, что к тому моменту, когда запись в множество начинается по новому кругу, вероятность конфликта
с перезаписанными значениями минимальна. Методы add() и contains() объявлены как synchronized, чтобы избежать коллизий:
//: concurrency/SerialNumberChecker java // Кажущиеся безопасными операции с появлением потоков // перестают быть таковыми. // {Args- 4}
import java util.concurrent *;
// Reuses storage so we don't run out of memory: class CircularSet {
private int[] array: private int len; private int index = 0; public CircularSet(int size) { array = new int[size], len = size.
// Инициализируем значением, которое не производится // классом SerialNumberGenerator for(int i =0; i < size; i++) array[i] = -1;
}
public synchronized void add(int i) { array[index] = i,
// Возврат индекса к началу с записью поверх старых значений: index = ++index % len.
}
public synchronized boolean contains(int val) { for(int i = 0; i < len; i++)
if(array[i] == val) return true; return false;
public class SerialNumberChecker {
private static final int SIZE = 10; private static CircularSet serials =
new CircularSet(lOOO); private static ExecutorService exec =
Подписывайтесь на наши страницы в социальных сетях.
Будьте в курсе последних книжных новинок, комментируйте, обсуждайте. Мы ждём Вас!
Похожие книги на "Философия Java3"
Книги похожие на "Философия Java3" читать онлайн или скачать бесплатно полные версии.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.
Отзывы о "Брюс Эккель - Философия Java3"
Отзывы читателей о книге "Философия Java3", комментарии и мнения людей о произведении.