private Random rand = new Random(47). protected synchronized Integer initialValueO { return rand.nextlnt(lOOOO).
}
}:
public static void incrementO {
value.set(value.get() + 1).
}
public static int get О { return value getO. } public static void main(String[] args) throws Exception {
ExecutorService exec = Executors newCachedThreadPoolО. for(int i = 0. i < 5. i++)
exec.execute(new Accessor(i)). TimeUnit.SECONDS.sleep(3); // Небольшая задержка exec shutdownNowO, // Выход из всех объектов Accessor
}
} /* Output #0 9259 #1- 556 #2. 6694 #3- 1862 #4: 962 #0: 9260 #1- 557 #2: 6695 #3: 1863 #4: 963
*///:-
Объекты ThreadLocal обычно хранятся в статических полях. Если вы создаете объект ThreadLocal, для обращения к содержимому объекта можно использовать только методы get() и set(). Метод get() возвращает копию объекта, ассоциированного с потоком, a set() сохраняет свой аргумент в объекте потока, возвращая ранее хранившийся объект. Их использование продемонстрировано в методах increment() и get() класса ThreadLocalVariableHolder. Обратите внимание: методы increment^) и get() не синхронизированы, потому что ThreadLocal не гарантирует отсутствия «ситуации гонки».
Взаимодействие между потоками
Итак, мы выяснили, что потоки способны конфликтовать друг с другом, и разобрались с тем, как предотвратить такие конфликты. Следующим шагом должно стать изучение возможностей взаимодействия между потоками. Ключевым моментом в этом процессе является подтверждение связи, безопасно реализуемое методами wait() и notify() класса Object. В многопоточной библиотеке Java SE5 также присутствуют объекты Condition с методами await() и signal(). Мы рассмотрим некоторые возникающие проблемы и их решения.
Методы wait() и notifyAII()
Метод wait() ожидает изменения некоторого условия, неподконтрольного для текущего метода. Довольно часто это условие изменяется в результате выполнения другой задачи. Активное ожидание, то есть проверка условия в цикле, нежелательно из-за неэффективного расходования вычислительных ресурсов. Таким образом, метод wait() обеспечивает механизм синхронизации действий между задачами.
Важно понять, что метод sleep() не освобождает объект блокировки. С другой стороны, метод wait() снимает блокировку с объекта, тем самым позволяя остальным потокам вызывать другие синхронизированные методы объекта во время выполнения wait(). Это очень важно, потому что обычно именно «другие» методы приводят к изменению условия и активизации приостановленной задачи.
Существует две формы метода wait(). У первой формы аргумент имеет такой же смысл, как и аргумент метода sleep(): это продолжительность интервала в миллисекундах, на который приостанавливается выполнение потока. Разница между методами состоит в следующем:
1. При выполнении метода wait() блокируемый объект освобождается.
2. Выйти из состояния ожидания, установленного wait(), можно двумя способами: с помощью уведомления notify() или notifyAU() либо по истечении срока ожидания.
Вторая, более распространенная форма вызывается без аргументов. Эта версия метода wait() заставит поток простаивать, пока не придет уведомление notify() или notifyAll().