본문 바로가기

Programming/Java

Thread Synchronized


공유
아래와 같은 방식으로 run()에서 공유할 객체에 접근하는 것이 가능해진다. 

예) 쓰레드가 객체를 공유하게 되는 방식 1 - Runnable 인터페이스 상속

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
import java.util.*;
 
class example {
    public static void main(String[] args) {
        RunnableImpl r = new RunnableImpl();
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
         
        t1.start();
        t2.start();
    }
}
 
class RunnableImpl implements Runnable {
    int iv = 0;
 
    @Override
    public void run() {
        int lv = 0;
        String name = Thread.currentThread().getName();
 
        while (lv < 3) {
            System.out.println(name + " Local Var:" + ++lv);
            System.out.println(name + " Instance Var:" + ++iv);
            System.out.println();
        }
    }
}

결과)

Thread-0 Local Var:1
Thread-0 Instance Var:1

Thread-0 Local Var:2
Thread-0 Instance Var:2

Thread-0 Local Var:3
Thread-0 Instance Var:3

Thread-1 Local Var:1
Thread-1 Instance Var:4

Thread-1 Local Var:2
Thread-1 Instance Var:5

Thread-1 Local Var:3
Thread-1 Instance Var:6


예) 쓰레드가 객체를 공유하게 되는 방식 2 - Thread 상속

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
import java.util.*;
 
class example {
    public static void main(String[] args) {
        Data d = new Data();
        MyThread t1 = new MyThread(d);
        MyThread t2 = new MyThread(d);
         
        t1.start();
        t2.start();
    }
}
 
class Data {
    int iv =0;
}
 
class MyThread extends Thread{
     
    Data d;
     
    MyThread(Data d) {
        this.d = d;
    }
 
    @Override
    public void run() {
        int lv = 0;
 
        while (lv < 3) {
            System.out.println(getName() + " Local Var:" + ++lv);
            System.out.println(getName() + " Instance Var:" + ++d.iv);
            System.out.println();
        }
    }
}

결과) 

Thread-0 Local Var:1
Thread-0 Instance Var:1

Thread-0 Local Var:2
Thread-0 Instance Var:2

Thread-0 Local Var:3
Thread-0 Instance Var:3

Thread-1 Local Var:1
Thread-1 Instance Var:4

Thread-1 Local Var:2
Thread-1 Instance Var:5

Thread-1 Local Var:3
Thread-1 Instance Var:6

 
 
동기화(Synchronized) 
 - 공유 데이터에 lock을 걸어 작업중이던 쓰레드가 마칠때까지 다른 쓰레드에게 제어권이 넘어가지않게 보호한다.
 - synchronized 블럭이 끝나면 lock이 풀리고 다른 쓰레드도 접근가능하게 된다.
 - 교착상태(dead-lock)에 빠질 위험이 있으므로 주의한다.

synchronized를 이용한 동기화 방법 
 - 가능하면 메서드에 synchronized를 사용하는 메서드 단위의 동기화를 권장

1
2
3
4
5
6
7
8
9
1. 특정한 객체에 lock을 걸고자 할 때
synchronized(객체의 참조변수){
    // ...
}
 
2. 메서드에 lock을 걸고자할 때
public synchronized void calcSum(){
    // ...
}
 


예) 동기화 적용 전 : if문을 통과하고 출금을 수행하려는 순간 다른 쓰레드에게 제어권이 넘어가서 다른 쓰레드가 출금을 해버렸고(잔고:0) 또 이전의 쓰레드로 제어권이 넘어오면서 출금을 수행했기 때문에 잔고가 음수가 되어버렸다.

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
import java.util.*;
 
class example {
    public static void main(String[] args) {
        MyThread r = new MyThread();
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
         
        t1.start();
        t2.start();
    }
}
 
class Account{
    int balance = 1000;
     
    public void withDraw(int money){
        if(balance >= money){
            try{
                Thread.sleep(1000);
            }catch (Exception e) {}
            balance -= money;
        }
    }
}
 
class MyThread implements Runnable{
    Account acc = new Account();
     
    @Override
    public void run() {
        while(acc.balance > 0){
            // 100, 200, 300 중의 한 값을 임의로 선택해서 출금(withDraw)
            int money = (int)(Math.random() * 3 + 1) * 100;
            acc.withDraw(money);
            System.out.println("balance:" + acc.balance);
        }
    }
}

결과) 

balance:500
balance:500
balance:300
balance:0
balance:-300



예) 동기화 적용 후

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
import java.util.*;
 
class example {
    public static void main(String[] args) {
        MyThread r = new MyThread();
        Thread t1 = new Thread(r);
        Thread t2 = new Thread(r);
         
        t1.start();
        t2.start();
    }
}
 
class Account{
    int balance = 1000;
     
    // 동기화 적용 (메서드 단위의 동기화를 추천한다.)
    public synchronized void withDraw(int money){
        /* 객체에 lock을 걸 경우
        synchronized (this) {
            여기에 소스 코드를 집어 넣어도 된다.
        }
        */
        if(balance >= money){
            try{
                Thread.sleep(1000);
            }catch (Exception e) {}
            balance -= money;
        }
    }
}
 
class MyThread implements Runnable{
    Account acc = new Account();
     
    @Override
    public void run() {
        while(acc.balance > 0){
            // 100, 200, 300 중의 한 값을 임의로 선택해서 출금(withDraw)
            int money = (int)(Math.random() * 3 + 1) * 100;
            acc.withDraw(money);
            System.out.println("balance:" + acc.balance);
        }
    }
}

결과)
balance:700
balance:500
balance:400
balance:200
balance:0
balance:0

Lock Flag : 자바 객체마다 한개씩 있는 키. 인스턴스 메소드마다 존재하는 키가 하나씩 있음.
Lock Pool : Lock된 코드를 만난 쓰레드가 대기하는 장소



'Programming > Java' 카테고리의 다른 글

[swing] Event Dispatch Thread  (0) 2016.02.04
serializable  (0) 2016.01.29
vector  (0) 2016.01.22
String  (0) 2016.01.21
equals, hashcode  (0) 2016.01.21