쓰레드의 동기화
싱글쓰레드 프로세스의 경우 프로세스 내에 단 하나의 쓰레드만 작업하기 때문에 프로세스의 자원을 가지고 작업하는데 별 문제가 없지만, 멀티 쓰레드의 프로세스의 경우 여러 쓰레드가 같은 프로세스 내의 자원을 공유해서 작업하기 때문에 서로 작업에 영향을 주게 됩니다.
※ 작업하던 도중에 다른 쓰레드B에게 제어권이 넘어 갔을 경우, 쓰레드A가 하던 작업을 쓰레드가B가 임의로 변경하면 다시 쓰레드A가 제어권을 받아서 나머지 작업을 마쳤을 때 원래 의도한 것과 다른 결과를 얻을 수 있습니다. 그래서 해당 작업에 lock을 걸어 쓰레드B가 해당 작업에 접근할 수 없도록 해야합니다.
synchronized를 이용한 동기화
자바에서 키워드 synchronized를 통해 해당 작업과 공유데이터에 lock을 걸어서 먼저 작업 중이던 쓰레드가 작업을 완전히 마칠 때까지는 다른 쓰레드에게 제어권이 넘어가더라도 데이터가 변경되지 않도록 보호함으로써 쓰레드의 동기화를 가능하게 합니다.
※ synchronized는 두 쓰레드가 lock을 건 상태에서 서로 lock이 풀리기 기다리는 교착상태 (dead lock)에 빠질 수 있다는 점을 주의해야합니다.
※ 작업도중에 쓰레드를 종료시킬 때는 작업하고 있던 객체를 lock을 푸는 것만 아니라 작업 중에 변경했던 공유데이터를 작업이전의 상태로 돌려놓는 것 까지 고려해야합니다.
1 2 3 4 5 6 7 8 9 | // 특정 객체에 lock을 걸고자 할 때 synchronized(객체의 참조변수){ // ... } // 메서드에 lock을 걸고자할 때 public synchronized void calcSum(){ // ... } | 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 35 36 37 38 39 40 41 42 43 44 45 46 | // Runnable 인터페이스를 이용한 객체 공유 class ThreadEx{ 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; // Thread가 공유하는 변수 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(); } } // run() } /* 실행결과 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 */ | 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 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | // synchronized를 이용한 객체 공유 class ThreadEx{ 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 Account{ int balance = 1000; public void withdraw(int money){ if(balance >= money){ try{ Thread.sleep(1000); }catch(Exception e){ } balance -= money; } } // withdraw } class RunnableImpl implements Runnable{ Account acc = new Account(); public void run(){ // 잔고가 출금하려는 금액보다 큰 경우에만 출금 while(acc.balance > 0){ // 100, 200, 300중의 한 값을 임으로 선택해서 출금 int money = (int)(Math.random() * 3 + 1) * 100; acc.withdraw(money); System.out.println("balance : "+acc.balance); } } // run() } /* 실행결과 balance : 700 balance : 400 balance : 200 balance : 0 balance : -100 */ /* 하지만 결과를 보면 잔고가 음수인 것을 볼 수 있습니다. 이유는 if문 조건식을 통과하고 출금하기 바로 직전에 다른 쓰레드가 끼어들어서 출금을 먼저 했기 때문입니다. 이렇게 한 쓰레드의 작업이 다른 쓰레드에 영향을 받는 일이 생기기 때문에 동기화가 반드시 필요합니다. withdraw 메소드에 synchronize 키워드를 붙이기만 하면 간단히 동기화가 됩니다. */ public synchronized void withdraw(int money){ if(balance >= money){ try{ Thread.sleep(1000); }catch(Exception e){ } balance -= money; } } // withdraw | cs |
[출처] [java] 쓰레드의 동기화|작성자 Simpolor
'JAVA > JAVA 스레드' 카테고리의 다른 글
[java] wait()와 notify() (0) | 2017.11.02 |
---|---|
[java] 쓰레드의 실행제어 (0) | 2017.11.02 |
[java] 데몬 쓰레드 (0) | 2017.11.01 |
[java] 쓰레드 그룹 (0) | 2017.11.01 |
[java] 쓰레드의 우선순위 (0) | 2017.11.01 |
[java] 싱글쓰레드와 멀티쓰레드 (0) | 2017.11.01 |
[java] start()와 run() (0) | 2017.11.01 |
[java] 쓰레드 구현 및 실행 (0) | 2017.11.01 |