//: concurrency/IntGenerator.java
public abstract class IntGenerator {
private volatile boolean canceled = false; public abstract int nextO; // Для отмены:
public void cancel О { canceled = true; } public boolean isCanceledO { return canceled; }
} III ~
IntGenerator содержит метод cancel(), изменяющий состояние флага canceled, и метод isCanceled(), проверяющий, был ли объект отменен. Поскольку флаг canceled относится к типу boolean, простые операции вроде присваивания и возврата значения выполняются атомарно, то есть без возможности прерывания, и вы не увидите поле в промежуточном состоянии между этими простыми операциями. Смысл ключевого слова volatile будет объяснен позже в этой главе.
Для тестирования IntGenerator можно воспользоваться следующим классом EvenChecker:
//. concurrency/EvenChecker.java import java.util.concurrent *,
public class EvenChecker implements Runnable { private IntGenerator generator, private final int id,
public EvenChecker(IntGenerator g. int ident) { generator = g. id = ident;
}
public void run О {
while({generator isCanceledO) {
int val = generator.nextO; if(val % 2 \= 0) {
System.out.println(val + " не четно!"); generator cancel О; // Отмена всех EvenChecker
}
// Тестирование произвольного типа IntGenerator-public static void test(IntGenerator gp. int count) {
System out.println("Ha>KMme Control-С. чтобы завершить программу"), ExecutorService exec = Executors.newCachedThreadPoolО; for(int i = 0; i < count; i++)
exec.execute(new EvenChecker(gp, i)); exec.shutdownO;
}
// Значение по умолчанию для count: public static void test(IntGenerator gp) { test(gp. 10);
}
} ///-
Как видно из run(), все задачи EvenChecker, зависящие от объекта IntGenerator, проверяют, не были ли они отменены. При таком подходе задачи, совместно использующие общий ресурс (IntGenerator), наблюдают за ресурсом, ожидая от него сигнала завершения. Тем самым устраняется так называемая «ситуация гонки», когда две и более задачи торопятся отреагировать на некоторое условие; это приводит к возникновению конфликтов или получению других некорректных результатов. Будьте внимательны, постарайтесь продумать все возможные сбои в системах с параллельным выполнением и защититься от них. Например, задача не может зависеть от другой задачи, потому что порядок завершения задач не гарантирован. Зависимость задач от объекта, не являющегося задачей, устраняет потенциальную «ситуацию гонки».
Метод test() настраивает и тестирует произвольный тип IntGenerator, запуская несколько задач EvenChecker с общим IntGenerator. Если IntGenerator приводит к сбою, test() сообщает о происходящем и возвращает управление. В противном случае его следует завершить вручную клавишами Ctrl+C.
Задачи EvenChecker постоянно читают и проверяют значения, полученные от IntGenerator. Если generator.isCanceled() равен true, run() возвращает управление; это сообщает Executor в EvenChecker.test() о том, что задача завершена. Любая задача EvenChecker может вызвать cancel() для связанного с ней IntGenerator, в результате чего все остальные EvenChecker, использующие IntGenerator, будут корректно завершены. Как будет показано далее в этой главе, в Java существуют и более общие механизмы завершения потоков.