데드락
2개의 쓰레드가 서로 기다리는 상태
이것을 방지하기 위해서 synchronized 메소드에서 다른 synchronized메소드를 부르면 안된다.
Wait() / notify() - wait(멈춰!)
Wait() : 리소스가 더 이상 유효하지 않은 경우 리소스가 사용 가능할 때 까지 위해
Thread를 non-runnable 상태로 전환한다.
Wait() 상태가 된 thread 는 notify() 호출할때 까지 기다린다.
Notify() : wait() 하고 있는 thread 중 아무거나 한 thread를 runnable 한 상태로 깨운다.
- 불공평
notifyAll() : wait() 하고 있는 모든 thread 가 runnable 한 상태가 되도록 한다.
Notify()보다 notifyAll()을 사용하기를 권장한다 특정 thread가 통지를 받도록 제어
할수 없으므로 모두 깨운 후에 scheduler 에 CPU를 점유하는 것이 공평하다고 한다.
데드락을 방지하기 위해서 wait() 으로 멈추고 notify 로 깨워야 한다.
package thread;
import java.util.ArrayList;
class FastLibrary {
public ArrayList<String> books = new ArrayList<String>();
public FastLibrary() { // 책을 추가한다.
books.add("태백산맥1");
books.add("태백산맥2");
books.add("태백산맥3");
books.add("태백산맥4");
books.add("태백산맥5");
books.add("태백산맥6");
}
public String lendBook() { // 책을 빌린다.
// 어떤스레드가 빌리고 어떤스레드가 가져가는지 정보를 알려면
Thread t = Thread.currentThread();
String title = books.remove(0);// 맨앞에있는 책을 빌린다.
System.out.println(t.getName() + " : " + title + " lend");
return title;
}
public void returnBook(String title) { // 책을 반납한다.
// 어떤 책이 반납했는지 알려면
Thread t = Thread.currentThread();
books.add(title);
System.out.println(t.getName() + " : " + title + " return");
}
}
class Student extends Thread {
public void run() {
String title = LibraryMain.library.lendBook(); // 학생이 책을 하나 빌림
try {
sleep(5000); // 5초동안 책을 읽음
LibraryMain.library.returnBook(title);// 다 읽고 책을 반납
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class LibraryMain {
// shard Resource
public static FastLibrary library = new FastLibrary();
public static void main(String[] args) {
// 학생 3명
Student std1 = new Student();
Student std2 = new Student();
Student std3 = new Student();
std1.start();
std2.start();
std3.start();
}
}
만약 리소스가 6명이고 한정된 책이 3권이면 ?
즉 동시에 동기화를 해주면 동시에 빌리지 않을 것이고
임계영역에 대한 동기화가 잘될 것이다.
package thread;
// 리소스가 한정적인데
// 학생6명이 있는 상태에서 책이 3권밖에 없을때
import java.util.ArrayList;
class FastLibrary2 {
public ArrayList<String> books = new ArrayList<String>();
public FastLibrary2() {
books.add("태백산맥1");
books.add("태백산맥2");
books.add("태백산맥3");
}
// 동기화를 해주면 동시에 빌리지 않도록 할수있다.
// 임계영역에 대한 동기화가 잘 될 것이다.
public synchronized String lendBook() throws InterruptedException {
Thread t = Thread.currentThread();
if(books.size() == 0) {
// 책이 오면 연락하게끔 기다리는게 wait 이다.
System.out.println(t.getName() + " waiting start");// 어떤스레드가 기다리는지
wait(); // 현재 currentThread 를 기다리게 한다.
System.out.println(t.getName() + " waiting end"); // 어떤 스레드가 끝났는지
}
// 여기서 리소스가 가능하지 않으면 못빌리게 해놓는다.
// if(books.size() == 0) return null;
String title = books.remove(0);
System.out.println(t.getName() + " : " + title + " lend");
return title;
}
public synchronized void returnBook(String title) {
Thread t = Thread.currentThread();
books.add(title);
// notify 는 여기서 호출시킨다.
notify(); // 책이 왔다.
System.out.println(t.getName() + " : " + title + " return");
}
}
class Student2 extends Thread {
public void run() {
String title;
try {
title = LibraryMain2.library2.lendBook();
if(title == null) return; // 책이없으면 못빌린거로 리턴 시킨다.
LibraryMain2.library2.returnBook(title);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// 책이 오면 연락하게끔 기다리는게 wait 이다.
// 책이 왔음을 알려주는게 notify 이다.
}
}
public class LibraryMain2 {
// shard Resource
public static FastLibrary2 library2 = new FastLibrary2();
public static void main(String[] args) {
// 학생 6명
Student2 std1 = new Student2();
Student2 std2 = new Student2();
Student2 std3 = new Student2();
Student2 std4 = new Student2();
Student2 std5 = new Student2();
Student2 std6 = new Student2();
std1.start();
std2.start();
std3.start();
std4.start();
std5.start();
std6.start();
}
}
스레드에 대한 정보를 얻을수있도록 하는 CurrentThread 를 불러오고
book 개수가 만약 0이면 wait() 을 줘서 현재 CurrentThread를 기다리게 한다.
그리고 학생은 책이 없으면 반환시킨다.
synchronized void returnBook 에서
books.add(title);
notify(); 를 호출시켜서 책이 왔음을 호출시킨다.
근데 문제점은 notify()는 아무 스레드가 랜덤으로 꺠우기 떄문에 평생 못깨어나는 스레드가 발생한다
그러므로 while을 준다.
모두다 깨운 다음에 while(books.size() == 0) 을 줘서
못빌린 애들은 다시 wait 상태로 빠트린다.
package thread;
// 리소스가 한정적인데
// 학생6명이 있는 상태에서 책이 3권밖에 없을때
import java.util.ArrayList;
class FastLibrary3 {
public ArrayList<String> books = new ArrayList<String>();
public FastLibrary3() {
books.add("태백산맥1");
books.add("태백산맥2");
books.add("태백산맥3");
}
// 동기화를 해주면 동시에 빌리지 않도록 할수있다.
// 임계영역에 대한 동기화가 잘 될 것이다.
public synchronized String lendBook() throws InterruptedException {
Thread t = Thread.currentThread();
while (books.size() == 0) { // notifyAll 일시 if에서 while 로 바꾼다.
// 못빌린 애들은 다시 wait 상태로 빠트린다. while로해서 다시 잠든 게끔 한다.
System.out.println(t.getName() + " waiting start");
wait();
System.out.println(t.getName() + " waiting end");
}
String title = books.remove(0);
System.out.println(t.getName() + " : " + title + " lend");
return title;
}
public synchronized void returnBook(String title) {
Thread t = Thread.currentThread();
books.add(title);
notifyAll(); // 모든 스레드가 깨어난다. 하나만 반환하면 모든 스레드가 꺠어나면 문제가 되는데
// while 로 바꾼다.
System.out.println(t.getName() + " : " + title + " return");
}
}
class Student3 extends Thread {
public void run() {
try {
String title = LibraryMain3.library3.lendBook();
if(title == null) return; // 책이없으면 못빌린거로 리턴 시킨다.
sleep(5000);
LibraryMain3.library3.returnBook(title);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
// 책이 오면 연락하게끔 기다리는게 wait 이다.
// 책이 왔음을 알려주는게 notify 이다.
}
}
public class LibraryMain3 {
// shard Resource
public static FastLibrary3 library3 = new FastLibrary3();
public static void main(String[] args) {
// 학생 6명
Student3 std1 = new Student3();
Student3 std2 = new Student3();
Student3 std3 = new Student3();
Student3 std4 = new Student3();
Student3 std5 = new Student3();
Student3 std6 = new Student3();
std1.start();
std2.start();
std3.start();
std4.start();
std5.start();
std6.start();
}
}
'JAVA Programming' 카테고리의 다른 글
[89] 이클립스 스킨 다운 방법 , 자바 API 보는 방법 , 이클립스 단축키 (0) | 2020.08.07 |
---|---|
[88] MultiThread 의 deadlock 과 wait() , notify() 와 notifyAll()- while사용 차이 (0) | 2020.08.04 |
[87] Multi Thread 임계영역 , 동기화 2가지 방법(메소드,블록) (0) | 2020.08.04 |
[86] Thread 종료하기 . boolean 으로 while문 true 시 인터럽트발생 (0) | 2020.08.03 |
[85] Thread 멀티 기타메소드CurrentThread() , wait() , join() , (0) | 2020.08.03 |