Взаимодействие между потоками JAVA или задачка 'Робот'
В процессе профессионально деятельности встретилась задачка. К своему стыду решить её на вскидку не смог :( Так что решил заполнить пробелы в знаниях статьей....Задачка
public class Main {
public static void main(String[] args) {
new Thread(new Leg("left")).start();
new Thread(new Leg("right")).start();
Thread.sleep(2000);
}
}
class Leg implements Runnable{
private String name;
public Leg(String name){
this.name = name;
}
@Override
public void run() {
while (true) {
System.out.println(name);
}
}
}
Решение I (wait/notify)
Для решения задачи будем использовать встроенные средства синхронизации потоков языка java - монитор и блоки synchronized. А также методы взаимодействия потоков (wait(), notify()).
Основное назначение перечисленных выше средств следующее:
Основное назначение перечисленных выше средств следующее:
- монитор - объект осуществляющий контроль доступа потоков к исполнению кода в критических секциях.
- блок synchronized - определяет границы критических секций монитора т.е участков кода в которых может исполнятся одновременно только один поток .
- метод wait - используется в критических секциях для остановки исполнения потока с освобождением блокировки монитора (т.е другой поток ожидающий входа в критическую секцию монитора после выполнения метода wait сможет начать исполнение критической секции монитора)
- метод notify - используется в критических секциях для восстановления работы одного из потоков остановленных в данном мониторе.
public class WaitNotifyMain {
public static void main(String[] args) {
Object monitor = new Object();
new Thread(new Leg("left",monitor)).start();
new Thread(new Leg("right",monitor)).start();
Thread.sleep(2000);
}
}
class Leg implements Runnable{
private String name;
private Object monitor;
public Leg(String name,Object monitor){
this.name = name;
this.monitor = monitor;
}
@Override
public void run() {
try{
while (true) {
synchronized (monitor) {
monitor.notify();
monitor.wait();
System.out.println(name);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Решение II (Condition)
Решение основано на классе ReentrantLock который представляет собой “Усовершенствование synchronized java”, появившейся в jdk1.5
public class CoditionMain {
public static void main(String[] args) {
final Lock lock = new ReentrantLock();
final Condition monitor = lock.newCondition();
System.out.println("Hello world!!");
new Thread(new LegCodition("left",
monitor,lock)).start();
new Thread(new LegCodition("right",
monitor,lock)).start();
Thread.sleep(2000);
}
}
class LegCodition implements Runnable{
private String name;
private Condition monitor;
private Lock lock;
public LegCodition(String name,
Condition monitor,Lock lock){
this.name = name;
this.lock=lock;
this.monitor = monitor;
}
@Override
public void run() {
lock.lock();
try {
while (true) {
monitor.signal();
monitor.await();
System.out.println(name);
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
finally {
lock.unlock();
}
}
}
Решение III (Queue)
Это все лишь нестандартное применение шаблона producer-consumer на основе LinkedBlockingQueue
public class QueueMain {
public static void main(String[] args){
LinkedBlockingQueue
queueToLeft = new LinkedBlockingQueue();
LinkedBlockingQueue
queueToRigth = new LinkedBlockingQueue();
queueToRigth.put(new Object());
new Thread(new LeftLeg("left",
queueToLeft,
queueToRigth)).start();
new Thread(new RightLeg("right",
queueToLeft,
queueToRigth)).start();
Thread.sleep(2000);
}
}
class LeftLeg implements Runnable{
private String name;
private LinkedBlockingQueue queueToLeft;
private LinkedBlockingQueue queueToRigth;
public LeftLeg(String name,
LinkedBlockingQueue queueToLeft,
LinkedBlockingQueue queueToRigth){
this.name = name;
this.queueToLeft = queueToLeft;
this.queueToRigth=queueToRigth;
}
@Override
public void run() {
try{
while (true) {
synchronized (this) {
Object obj=queueToRigth.take();
System.out.println(name);
queueToLeft.put(queueToLeft);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class RightLeg implements Runnable{
private String name;
private LinkedBlockingQueue queueToLeft;
private LinkedBlockingQueue queueToRigth;
public RightLeg(String name,
LinkedBlockingQueue queueToLeft,
LinkedBlockingQueue queueToRigth){
this.name = name;
this.queueToLeft = queueToLeft;
this.queueToRigth=queueToRigth;
}
@Override
public void run() {
try{
while (true) {
synchronized (this) {
Object obj= queueToLeft.take();
System.out.println(name);
queueToRigth.put(queueToLeft);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Ресурсы
1. Статья "Синхронизация потоков"2. Брюс Эккель. Философия Java = Thinking in Java. — 3-е изд. — СПб.: Питер, 2003. — 976 с. — ISBN 5-88782-105-1
3. Статья Использование языка Java для разработки параллельных приложений