×
Traktatov.net » Философия Java » Читать онлайн
Страница 362 из 395 Настройки

// Инициализируем значением, которое не производится // классом 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 =

Executors.newCachedThreadPool(), static class SerialChecker implements Runnable { public void run() {

while(true) {

int serial =

Seri alNumberGenerator.nextSeri alNumber(); if(serials.contains(serial)) {

System, out. pri ntl nCDuplicate: " + serial); System.exit(O);

}

serials.add(serial);

}

}

}

public static void main(String[] args) throws Exception { for(int i = 0; i < SIZE, i++)

exec, execute (new SerialCheckerO); // Остановиться после n секунд при наличии аргумента:

if(args length > 0) {

TimeUnit SECONDS sleep(new lnteger(args[0])). System out printin("No duplicates detected"), System exit(0).

}

}

} /* Output Duplicate 8468656 *///•-

В классе SerialNumberChecker содержится статическое поле CircuLarSet, хранящее все серийные номера, и вложенный поток Thread, который получает эти номера и удостоверяется в их уникальности. Создав несколько потоков, претендующих на серийные номера, вы обнаружите, что какой-нибудь из них довольно быстро получит уже имеющийся номер (заметьте, что на вашей машине программа может и не обнаружить конфликт, но на многопроцессорной системе она успешно их нашла). Для решения проблемы добавьте к методу nextSe-rialNumber() слово synchronized.

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

Атомарные классы

В Java SE5 появились специальные классы для выполнения атомарных операций с переменными — Atomiclnteger, AtomicLong, AtomicReference и т. д. Эти классы содержат атомарную операцию условного обновления в форме

boolean compareAndSer(expectedValue, updateValue),

Эти классы предназначены для оптимизации с целью использования атомарности на машинном уровне на некоторых современных процессорах, поэтому в общем случае вам они не понадобятся. Иногда они применяются и в повседневном программировании, но только при оптимизации производительности. Например, версия AtomicityTest.java, переписанная для использования Atomic-Integer, выглядит так:

// concurrency/AtomicIntegerTest java import java.util concurrent *. import java util concurrent atomic *; import java.util.*.

public class AtomicIntegerTest implements Runnable { private Atomiclnteger i = new AtomicInteger(O), public int getValueO { return i getO. } private void evenIncrement() { i addAndGet(2), } public void runО { while(true)