Java에서 wait() 메서드와 notify() notifyall() 메서드 사용하기
wait() 메서드와 notify(), notifyAll() 메서드의 상호관계
wait()
, notify()
, notifyAll()
은 Java에서 스레드 간의 상호작용을 위해 사용되는 메서드입니다. 이들 메서드는 Object
클래스에 정의되어 있으며, synchronized
키워드를 사용하여 동기화된 메서드 또는 블록 내에서 호출해야 합니다.
wait()
: 스레드가 대기 상태로 들어가며, 다른 스레드에 의해notify()
또는notifyAll()
이 호출될 때까지 기다립니다.wait()
을 호출하면 현재 스레드가 해당 객체의 모니터 락을 놓고 대기하게 됩니다.notify()
: 대기 중인 스레드 중 하나를 임의로 선택하여 깨웁니다. 선택된 스레드는 실행 가능한 상태가 되지만, 모니터 락을 얻기 위해 경쟁해야 합니다. 여러 개의 스레드가 대기 중인 경우, 어떤 스레드가 깨어날지는 알 수 없습니다.notifyAll()
: 대기 중인 모든 스레드를 깨웁니다. 대기 중인 모든 스레드가 실행 가능한 상태가 되지만, 모니터 락을 얻기 위해 경쟁해야 합니다.
notify()
와 notifyAll()
은 wait()
을 호출한 스레드를 깨울 수 있습니다. 그러나 notifyAll()
은 모든 대기 중인 스레드를 깨우므로, 경쟁 상황에서 성능 저하를 초래할 수 있습니다. 따라서, 필요한 경우에만 notify()
를 사용하는 것이 좋습니다.
또한, wait()
, notify()
, notifyAll()
은 반드시 동기화 블록 내에서 호출되어야 하며, 해당 객체의 모니터 락이 소유되어야 합니다. 그렇지 않으면 IllegalMonitorStateException
이 발생합니다.
예시코드
아래는 wait()
메서드와 notifyAll()
메서드를 사용하는 예시 코드입니다:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
class TestLibrary {
public List<String> books = new ArrayList<>();
private static TestLibrary library = null;
public static TestLibrary getLibrary() {
if (library == null) {
library = new TestLibrary();
}
return library;
}
public TestLibrary() {
books.add("자바의 정석1");
books.add("자바의 정석2");
books.add("자바의 정석3");
}
public synchronized String rendBook() throws InterruptedException {
Thread currentThread = Thread.currentThread();
while (books.isEmpty()) { // 스레드가 깨어 난 후에도 books가 비어있을 수 있기 때문에 while문으로 감싸줌
System.out.println(currentThread.getName() + " waiting start");
wait(); // 스레드 대기
System.out.println(currentThread.getName() + " waiting end");
}
if (!books.isEmpty()) {
String book = books.remove(0);
System.out.println(currentThread.getName() + ": " + book + " rend ");
return book;
} else return null;
}
public synchronized void returnBook(String book) {
books.add(book);
notifyAll(); // 대기중인 스레드를 깨움
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName() + ": " + book + " return ");
}
}
class Student extends Thread {
public Student(String name) {
super(name);
}
@Override
public void run() {
try {
TestLibrary library = TestLibrary.getLibrary();
String title = library.rendBook();
if (title == null) {
System.out.println("빌리지 못함");
return; }
Thread.sleep(5000);
library.returnBook(title);
} catch (InterruptedException e) {
System.out.println(e);
}
}
}
public class ThreadWaitTest {
@Test
public void test() {
Student student1 = new Student("학생1");
Student student2 = new Student("학생2");
Student student3 = new Student("학생3");
Student student4 = new Student("학생4");
Student student5 = new Student("학생5");
Student student6 = new Student("학생6");
student1.start();
student2.start();
student3.start();
student4.start();
student5.start();
student6.start();
while (true); // 스레드 테스트이기 때문에 메인 스레드가 종료되지 않도록 함
}
}
위의 예시 코드는 도서관을 구현한 TestLibrary
클래스와, 도서관에서 책을 빌리는 학생을 구현한 Student
클래스, 그리고 테스트를 위한 ThreadWaitTest
클래스로 구성되어 있습니다. 책 대여시 rendBook()
메서드를 호출하면, 도서관에 책이 없으면 대기 상태로 들어가고, 책이 있으면 책을 빌립니다. 책 반납시 returnBook()
메서드를 호출하면, 책을 반납하고 대기 중인 스레드를 깨웁니다.
결과:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
학생2: 자바의 정석1 rend
학생1: 자바의 정석2 rend
학생3: 자바의 정석3 rend
학생4 waiting start
학생5 waiting start
학생6 waiting start
학생2: 자바의 정석1 return
학생4 waiting end
학생4: 자바의 정석1 rend
학생3: 자바의 정석3 return
학생1: 자바의 정석2 return
학생6 waiting end
학생6: 자바의 정석3 rend
학생5 waiting end
학생5: 자바의 정석2 rend
This post is licensed under CC BY 4.0 by the author.