Метод f() класса DualSync синхронизируется по объекту this (синхронизируя метод целиком), а метод g() использует синхронизацию посредством объекта syncObject. Таким образом, два варианта синхронизации независимы. Демонстрируется этот факт методом main(), в котором создается поток Thread с вызовом метода f(). Поток main() после этого вызывает метод д(). Из результата работы программы видно, что оба метода работают одновременно и ни один из них не блокируется соседом.
Локальная память потока
Второй механизм предотвращения конфликтов доступа к общим ресурсам основан на исключении их совместного использования.
За выделение локальной памяти потоков и управление ею отвечает класс java.lang.ThreadLocal:
//: concurrency/ThreadLocalVariableHolder.java
// Автоматическое выделение собственной памяти каждому потоку.
import java util.concurrent.*, import java util *,
class Accessor implements Runnable { private final int id. public Accessor(int idn) { id = idn; } public void run() {
while( IThread.currentThreadO islnterrupted()) { ThreadLocalVariableHolder incrementO; System out println(this). Thread.yieldO.
}
}
public String toStringO {
return "#" + id + " " +
ThreadLocalVari ableHolder.get().
public class ThreadLocalVariableHolder {
private static ThreadLocal
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(). У первой формы аргумент имеет такой же смысл, как и аргумент метода sleep(): это продолжительность интервала в миллисекундах, на который приостанавливается выполнение потока. Разница между методами состоит в следующем:
1. При выполнении метода wait() блокируемый объект освобождается.
2. Выйти из состояния ожидания, установленного wait(), можно двумя способами: с помощью уведомления notify() или notifyAU() либо по истечении срока ожидания.
Вторая, более распространенная форма вызывается без аргументов. Эта версия метода wait() заставит поток простаивать, пока не придет уведомление notify() или notifyAll().