Использование каналов для ввода/вывода между потоками
Часто бывает полезно организовать взаимодействие потоков посредством механизмов ввода/вывода. Библиотеки потоков могут предоставлять поддержку ввода/вывода между потоками в форме
Простой пример взаимодействия двух потоков через канал:
//• concurrency/PipedlO.java
// Использование каналов для ввода/вывода между потоками
import java.util.concurrent.*;
import java.io.*;
import java.util.*;
import static net.mindview.util.Print.*;
class Sender implements Runnable {
private Random rand = new Random(47); private PipedWriter out = new PipedWriterO; pubTic PipedWriter getPipedWriterO { return out, } public void run() { try {
while(true)
for(char с = 'А'; с <= 'z'; С++) { out write(c);
TimeUnit.MILLISECONDS.sleep(rand.nextlnt(500));
}
} catch(IOException e) {
print(e + " Sender write exception"); } catch(InterruptedException e) {
print(e + " Sender sleep interrupted");
}
}
class Receiver implements Runnable { private PipedReader in;
public Receiver(Sender sender) throws IOException {
in = new PipedReader(sender.getPipedWriterO).
}
public void run() { try {
while(true) {
// Блокируется до появления следующего символа, printnb("Read- " + (char)in.readO + ");
}
} catch(IOException e) {
print(e + " Receiver read exception");
public class PipedIO {
public static void main(String[] args) throws Exception { Sender sender = new SenderO; Receiver receiver = new Receiver(sender); ExecutorService exec = Executors.newCachedThreadPool0; exec.execute(sender); exec.execute(receiver); TimeUnit.SECONDS.sleep(4); exec.shutdownNowO;
}
} /* Output:
Read: A, Read: B. Read: C. Read: D, Read- E, Read. F, Read: G, Read: H, Read: I. Read: J. Read: K, Read: L, Read: M, java.lang.InterruptedException: sleep interrupted Sender sleep interrupted
java.io.InterruptedlOException Receiver read exception *///:-
Классы Sender и Receiver представляют задачи, которые должны взаимодействовать друг с другом. В классе Sender создается канал PipedWriter, существующий как автономный объект, однако при создании канала PipedReader в классе Receiver конструктору необходимо передать ссылку на PipedWriter. Sender записывает данные в канал Writer и бездействует в течение случайно выбранного промежутка времени. Класс Receiver не содержит вызовов sleep() или wait(), но при проведении чтения методом read() он автоматически блокируется при отсутствии данных.
Заметьте, что потоки sender и receiver запускаются из main()
Взаимная блокировка
Итак, потоки способны перейти в блокированное состояние, а объекты могут обладать синхронизированными методами, которые запрещают использование объекта до тех пор, пока не будет снята блокировка. Возможна ситуация, в которой один поток ожидает другой поток, тот, в свою очередь, ждет освобождения еще одного потока и т. д., пока эта цепочка не замыкается на поток, который ожидает освобождения первого потока. Получается замкнутый круг потоков, которые дожидаются освобождения друг друга, и никто не может двинуться первым. Такая ситуация называется
Если вы запускаете программу и в ней незамедлительно возникает взаимная блокировка, проблему удается немедленно отследить. По-настоящему неприятна ситуация, когда ваша программа по всем признакам работает прекрасно, но тем не менее в какой-то момент входит во взаимную блокировку. Такая опасность незаметно присутствует в программе, пока нежданно-негаданно не проявится у заказчика (и, скорее всего, легко воспроизвести эту ситуацию вам не удастся). Таким образом, тщательное проектирование программы с целью предотвращения взаимных блокировок — важнейшая часть разработки параллельных программ.
Классический пример взаимной блокировки, предложенный Эдгаром Дейк-строй —