
class Producer extends Thread{
private MyBox box;
public Producer(MyBox box) {
this.box = box;
}
public void run() {
for (int i = 0; i < 20; i++) {
box.put(i);
try {
sleep(100);
}catch (InterruptedException e) {
}
}
}
}
class Consumer extends Thread{
private MyBox box;
public Consumer(MyBox c) {
box = c;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
box.get();
try {
sleep(100);
}catch (InterruptedException e) {
}
}
}
}
class MyBox {
private int contents;
private boolean isEmpty = true;
public synchronized void get() {
//박스가 비지 않았을 때
if(!isEmpty) {
isEmpty = !isEmpty;
System.out.println(Thread.currentThread().getName() + " : 소비 " + contents);
}
}
public synchronized void put(int value) {
//박스가 비었을 때
if(isEmpty) {
contents = value;
System.out.println(Thread.currentThread().getName() + " : 생산 " + value);
isEmpty = !isEmpty;
}
}
}
public class ProducerConsumer {
public static void main(String[] args) {
MyBox c = new MyBox();
Producer p1 = new Producer(c);
Consumer c1 = new Consumer(c);
Consumer c2 = new Consumer(c);
p1.start();
c1.start();
c2.start();
}
}
→ 위에서 동기화를 해도 컨트롤이 되지 않는다.
상호 배제는 되지만 원하는 순간에 원하는 메소드를 불러 내기는 어렵다.
이럴 때, wait 와 notify 를 사용한다.
class Producer extends Thread{
private MyBox1 box;
public Producer(MyBox1 box) {
this.box = box;
}
public void run() {
for (int i = 0; i < 20; i++) {
box.put(i);
try {
sleep(100);
}catch (InterruptedException e) {
}
}
}
}
class Consumer extends Thread{
private MyBox1 box;
public Consumer(MyBox1 c) {
box = c;
}
public void run() {
int value = 0;
for (int i = 0; i < 10; i++) {
box.get();
try {
sleep(100);
}catch (InterruptedException e) {
}
}
}
}
class MyBox1 {
private int contents;
private boolean isEmpty = true;
public synchronized int get() {
while(isEmpty) {
try {
// 만나면 lock을 풀고 not runnable 로 간다.
wait();
}catch (InterruptedException e) {
}
}
isEmpty = !isEmpty;
// 실행 불가상태로 된 것을 깨는 역할(대기열로 돌아온다.)
notifyAll();
System.out.println(Thread.currentThread().getName() + " : 소비 " + contents);
return contents;
}
public synchronized void put(int value) {
while(!isEmpty) {
try {
wait();
}catch (InterruptedException e) {
}
}
contents = value;
System.out.println(Thread.currentThread().getName() + " : 생산 " + value);
isEmpty = !isEmpty;
notifyAll();
}
}
public class ProducerConsumer {
public static void main(String[] args) {
MyBox1 c = new MyBox1();
Producer p1 = new Producer(c);
Consumer c1 = new Consumer(c);
Consumer c2 = new Consumer(c);
p1.start();
c1.start();
c2.start();
}
}
→ wait( ) : wait를 만나면 락을 풀고 실행 불가 상태로 빠진다.
→ notify, notifyAll( ) : 실행 불가 상태된 애들을 깨워서 대기열로 보내준다.
1) notify( ) : 실행 불가 상태인 애들 중에 무작위로 1개를 깨운다.
2) notifyAll( ) : 실행 불가 상태인 애들 전부를 깨운다.
이때, 무조건 다 깨우는 것이 아니라 누구의 락을 가지고 있는지 보고 깨운다.
예를 들어,
a,b 가 some의 락을 가지고 있고
c,d 가 other의 락을 가지고 있다고 하자.
notifyAll이 some의 락을 가지고 있다면 a,b 만 깨우는 것이다.
wait, notifyAll, notify -> Object에 있다.
자바 모든 객체가 공유자원으로 사용될 준비가 되어 있다.
모두 실행 불가 상태로 되어 버리는 것을 말한다.
스레드의 상태가 변하는 것이 아니다.
스레드의 상태는 계속 변하는데 코드에 진전이 없는 것을 말한다.

import static java.lang.System.out;
/* This is an example of livelock */
public class Dinner {
public static void main(String[] args) {
Spoon spoon = new Spoon();
Dish dish = new Dish();
new Thread(new Husband(spoon, dish)).start();
new Thread(new Wife(spoon, dish)).start();
}
}
class Spoon {
private boolean locked;
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
}
class Dish {
private boolean locked;
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
}
class Husband implements Runnable {
private Spoon spoon;
private Dish dish;
public Husband(Spoon spoon, Dish dish) {
this.spoon = spoon;
this.dish = dish;
}
@Override
public void run() {
while (true) {
synchronized (spoon) {
spoon.setLocked(true);
out.println("husband get spoon");
try { Thread.sleep(2000); } catch (InterruptedException e) {}
if (dish.isLocked()) {
spoon.setLocked(false); // give away spoon
out.println("husband pass away spoon");
} else {
synchronized (dish) {
dish.setLocked(true);
out.println("Husband is eating!");
}
dish.setLocked(false);
}
}
spoon.setLocked(false);
}
}
}
class Wife implements Runnable {
private Spoon spoon;
private Dish dish;
public Wife(Spoon spoon, Dish dish) {
this.spoon = spoon;
this.dish = dish;
}
@Override
public void run() {
while (true) {
synchronized (dish) {
dish.setLocked(true);
out.println("wife get dish");
try { Thread.sleep(2000); } catch (InterruptedException e) {}
if (spoon.isLocked()) {
dish.setLocked(false); // give away dish
out.println("wife pass away dish");
} else {
synchronized (spoon) {
spoon.setLocked(true);
out.println("Wife is eating!");
}
spoon.setLocked(false);
}
}
dish.setLocked(false);
}
}
}