quilt code
[고급자바] Thread(6) 본문
1. Lock Account
1) 은행의 입출금 처리를 스레드로 처리하는 예제 (Lock 객체를 이용한 동기화 처리)
| 락 기능을 제공하는 클래스 |
ReentrantLock : Read 및 Write 구분없이 사용하기 위한 락 클래스로 동기화 처리를 위해 사용된다. synchronize를 이용한 동기화 처리보다 부가적인 기능이 제공됨. ex) fairness 설정 등 . => 가장 오래된 기다린 스레드가 가장 먼저 락을 획득하게 함 |
| ReentrantReadWriteLock : Read 및 Write 락을 구분하여 사용가능함. 여러 스레드 동시에 Read 작업은 가능하지만 Write 작업은 단지 하나의 스레드만 가능하다. (exclusive) => Write보다 Read 위주의 작업이 많이 발생하는 경우에 사용하면 처리량(Throughput)이 좋아진다. |
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public static void main(String[] args) {
Lock lock = new ReentrantLock(true);
LockAccount lAcc = new LockAccount(lock);
lAcc.deposit(10000);
Thread th1 = new BankThread2(lAcc);
Thread th2 = new BankThread2(lAcc);
th1.start();
th2.start();
}
|
cs |
2) 입출금을 담당하는 클래스(공유객체)
|
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
|
class LockAccount {
private int balance;
// Lock객체 생성 => 되도록이면 private final로 만든다.
private final Lock lock;
public LockAccount(Lock lock) {
this.lock = lock;
}
public int getBalance() {
return balance;
}
// 입금 처리하는 메소드
public void deposit(int money) {
/*
* Lock 객체의 lock()메소드가 동기화 시작이고, unlock()메소드가 동기화의 끝을 나타낸다.
* lock()메소드로 동기화를 설정한 곳에서는 반드시 unlock()메소드로 해제해 주어야 한다.
*/
lock.lock(); // 락 설정(다른 스레드가 락을 획득하기 전까지 BLOCKED 됨.)
balance += money; // 동기화 처리 부분
lock.unlock(); // 락 해제
}
// 출금 처리하는 메소드(출금 성공: true, 출금 실패: false 반환함.)
public boolean withdraw(int money) {
boolean isOk = false;
/*
* try ~ catch 블럭을 사용할 경우에는 unlock() 메소드 호출은 finally 블럭에서 하도록 한다.
*/
try {
lock.lock(); // 락 설정
System.out.println(Thread.currentThread().getName() + "락 설정(획득) 성공");
if(balance >= money) {
for(int i=0; i<1000000000; i++) {} // 시간 때우기
balance -= money;
System.out.println("메소드 안에서 balance = " + getBalance());
isOk = true;
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock(); // 락 해제
System.out.println(Thread.currentThread().getName() + "락 해제(반납) 완료");
}
return isOk;
}
}
|
cs |
3) 은행 업무를 처리하는 스레드
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
class BankThread2 extends Thread {
private LockAccount lAcc;
public BankThread2(LockAccount lAcc) {
this.lAcc = lAcc;
}
@Override
public void run() {
boolean result = lAcc.withdraw(6000);
System.out.println(Thread.currentThread().getName() + " 스레드 안에서 result = " + result + ", balance = " + lAcc.getBalance());
}
}
|
cs |
2. 동기화 처리한 리스트 예제
Vector. Hashtable 등의 예전부터 존재하던 Collection 클래스들은 내부에 동기화 처리가 되어 있다. 그런데, 최근에 새로 구성된 클래스들은 동기화 처리가 되어 있지 않다. 그래서 동기화가 필요한 프로그램에서 이런 컬렉션 클래스들을 사용햐려면 동기화 처리를 한 후에 사용해야 한다. |
|
1
2
3
4
5
6
7
|
// 동기화 처리를 하지 않은 경우...
private static List<Integer> list1 = new ArrayList<>();
// 동기화 처리를 한 경우...
private static List<Integer> list2 = Collections.synchronizedList(new ArrayList<>());
private static List<Integer> list3 = new Vector<>();
|
cs |
|
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
|
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
for(int i=1; i<=10000; i++) {
//list1.add(i);
//list2.add(i);
list3.add(i);
}
}
};
Thread[] ths = new Thread[] {
new Thread(r), new Thread(r),
new Thread(r), new Thread(r), new Thread(r)
};
for(Thread th : ths) {
th.start();
}
for(Thread th : ths) {
try {
th.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//System.out.println("list1의 개수 : " + list1.size());
//System.out.println("list2의 개수 : " + list2.size());
System.out.println("list2의 개수 : " + list3.size());
}
|
cs |
3. Wait Notify
wait() : 동기화 영역에서 락을 풀고 Wait-Set영역(공유객체별 존재)으로 이동시킨다. |
notify() 또는 notifyAll() : Wait-Set 영역에 있는 스레드를 깨워서 실행될 수 있도록 한다. (notify()는 하나, notifyAll()은 Wait-Set에 있는 모든 스레드를 깨운다.) |
| => wait()와 notify(), notifyAll() 메소드는 동기화 영역에서만 실행할 수 있고, Object 클래스에서 제공하는 메소드이다. |
|
1
2
3
4
5
6
|
public static void main(String[] args) {
WorkObject workObj = new WorkObject();
new ThreadA(workObj).start();
new ThreadB(workObj).start();
}
|
cs |
1) 공통으로 사용할 객체
|
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
|
class WorkObject{
public synchronized void methodA() {
System.out.println("methodA()메소드 작업 중...");
System.out.println(Thread.currentThread().getName() + " : notify() 호출");
notify();
try {
System.out.println(Thread.currentThread().getName() + " : wait() 호출");
wait();
} catch(InterruptedException ex) {
ex.printStackTrace();
}
}
public synchronized void methodB() {
System.out.println("methodB()메소드 작업 중...");
System.out.println(Thread.currentThread().getName() + " : notify() 호출");
notify();
try {
System.out.println(Thread.currentThread().getName() + " : wait() 호출");
wait();
} catch(InterruptedException ex) {
ex.printStackTrace();
}
}
}
|
cs |
2) workObject의 methodA()만 호출하는 스레드
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class ThreadA extends Thread {
private WorkObject workObj;
public ThreadA(WorkObject workObj) {
super("ThreadA");
this.workObj = workObj;
}
@Override
public void run() {
for(int i=1; i<=10; i++) {
workObj.methodA();
}
System.out.println("ThreadA 종료.");
}
}
|
cs |
3) workObject의 methodB()만 호출하는 스레드
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class ThreadB extends Thread {
private WorkObject workObj;
public ThreadB(WorkObject workObj) {
super("ThreadB");
this.workObj = workObj;
}
@Override
public void run() {
for(int i=1; i<=10; i++) {
workObj.methodB();
}
System.out.println("ThreadB 종료.");
}
}
|
cs |
4. Wait Notify
|
1
2
3
4
5
6
7
8
9
|
public class T20WaitNotifyTest {
public static void main(String[] args) {
DataBox dataBox = new DataBox();
new ProducerThread(dataBox).start();
new ConsumerThread(dataBox).start();
}
}
|
cs |
1) 공통으로 사용할 클래스
|
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
|
class DataBox{
private String data;
// data가 null이 아닐 때 data값을 반환하는 메소드
public synchronized String getData() {
if(this.data == null) {
try {
System.out.println(Thread.currentThread().getName() + " wait() 호출");
wait();
} catch(InterruptedException ex) {
ex.printStackTrace();
}
}
String returnData = this.data;
System.out.println("읽어온 데이터 : " + returnData);
this.data = null; // 데이터비우기
System.out.println(Thread.currentThread().getName() + " notify() 호출");
notify();
return returnData;
}
// data가 null일 경우에만 자료를 세팅하는 메소드 (null이 아니면 설정할 필요x)
public synchronized void setData(String data) {
if(this.data != null) {
try {
System.out.println(Thread.currentThread().getName() + " wait() 호출");
wait();
} catch(InterruptedException ex) {
ex.printStackTrace();
}
}
this.data = data; // 데이터 설정
System.out.println("세팅한 데이터 : " + this.data);
System.out.println(Thread.currentThread().getName() + " notify() 호출");
notify();
}
}
|
cs |
2) 데이터를 세팅만 하는 스레드
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class ProducerThread extends Thread {
private DataBox dataBox;
public ProducerThread(DataBox dataBox) {
super("ProducerThread");
this.dataBox = dataBox;
}
@Override
public void run() {
for(int i=1; i<=5; i++) {
String data = "Data-" + i;
System.out.println("dataBox.setData(" + data + ") 호출");
dataBox.setData(data);
}
}
}
|
cs |
3) 데이터를 읽어만 오는 스레드
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
class ConsumerThread extends Thread {
private DataBox dataBox;
public ConsumerThread(DataBox dataBox) {
super("ConsumerThread");
this.dataBox = dataBox;
}
@Override
public void run() {
for(int i=1; i<=5; i++) {
String data = dataBox.getData();
System.out.println("dataBox.getData() : " + data);
}
}
}
|
cs |
'daily > 고급자바' 카테고리의 다른 글
| [고급자바] IO (2) (0) | 2023.02.13 |
|---|---|
| [고급자바] IO (1) (0) | 2023.02.13 |
| [고급자바] Thread(5) (0) | 2023.02.10 |
| [고급자바] Thread (4) (0) | 2023.02.10 |
| [고급자바] 컴퓨터와 가위바위보 프로그램 (0) | 2023.02.07 |