620 likes | 888 Views
객체지향프로그래밍 13 주차 – 멀티쓰레딩. 제 13 주 멀티쓰레딩 (Multithreading). 제 13 주 강의 목표 여러 쓰레드가 어떻게 병렬도 실행되는 지 이해함 쓰레드를 구현하는 법을 배움 레이스 컨디션과 데드락에 대해 배움 락과 컨디션을 사용함으로써 공유자료가 엉망이 되지 않도록 하는 법을 배움 애니메이션에 스레드를 이용하는 법을 배움. Thread. 스레드 는 프로그램을 병렬로 실행할 수 있게 해준다 병렬로 실행되는 쓰레드들은 프로그램 코드와 메모리 데이터를 공유한다 .
E N D
객체지향프로그래밍13주차 –멀티쓰레딩 강원대학교
제 13주멀티쓰레딩(Multithreading) • 제 13주 강의 목표 • 여러 쓰레드가 어떻게 병렬도 실행되는 지 이해함 • 쓰레드를 구현하는 법을 배움 • 레이스 컨디션과 데드락에 대해 배움 • 락과 컨디션을 사용함으로써 공유자료가 엉망이 되지 않도록 하는 법을 배움 • 애니메이션에 스레드를 이용하는 법을 배움 강원대학교
Thread • 스레드는 프로그램을 병렬로 실행할 수 있게 해준다 • 병렬로 실행되는 쓰레드들은 프로그램 코드와 메모리 데이터를 공유한다. • 각각의 스레드는 자기만의 스택과 레지스터를 가지고 있다 강원대학교
날짜 시간 출력 코드 final int REPETITIONS = 10; String greeting = "Hello!"; for (int i = 1; i <= REPETITIONS; i++) { Date now = new Date(); System.out.println(now + " " + greeting); Thread.sleep(DELAY); } 강원대학교
단일 쓰레드 실행 Fri May 28 15:21:35 KST 2010 Hello! Fri May 28 15:21:36 KST 2010 Hello! Fri May 28 15:21:37 KST 2010 Hello! Fri May 28 15:21:38 KST 2010 Hello! Fri May 28 15:21:39 KST 2010 Hello! Fri May 28 15:21:40 KST 2010 Hello! Fri May 28 15:21:41 KST 2010 Hello! Fri May 28 15:21:42 KST 2010 Hello! Fri May 28 15:21:43 KST 2010 Hello! Fri May 28 15:21:44 KST 2010 Hello! 강원대학교
두 쓰레드 실행 Fri May 28 15:24:09 KST 2010 Hello! Fri May 28 15:24:09 KST 2010 Hello! Fri May 28 15:24:10 KST 2010 Hello! Fri May 28 15:24:10 KST 2010 Hello! Fri May 28 15:24:11 KST 2010 Hello! Fri May 28 15:24:11 KST 2010 Hello! Fri May 28 15:24:12 KST 2010 Hello! Fri May 28 15:24:12 KST 2010 Hello! Fri May 28 15:24:13 KST 2010 Hello! Fri May 28 15:24:13 KST 2010 Hello! Fri May 28 15:24:14 KST 2010 Hello! Fri May 28 15:24:14 KST 2010 Hello! 강원대학교
Running a Thread • Runnable인터페이스를 구현하는 클래스를 정의하고 수행할 작업을 run메소드에 적어준다. public class MyRunnable implements Runnable { public void run() { // Task statements go here } } public interface Runnable{ void run();} 강원대학교
Running a Thread • 정의된 클래스 객체를 구성한다. • Runnable 객체를 인자로 삼아 Thread객체를 구성한다. • Thread에 start메소들를 호출한다. Runnable r = new MyRunnable(); Thread t = new Thread(r); t.start(); 강원대학교
GreetingRunnable 뼈대 public class GreetingRunnable implements Runnable { public GreetingRunnable(String aGreeting) { greeting = aGreeting; } public void run() { // Task statements go here . . . } // Fields used by the task statements private String greeting; } 강원대학교
GreetingRunnable의 run 메소드에서 할 일 • Print a time stamp • Print the greeting • Wait a second • 현재 날짜와 시간은 Date 객체를 구성함으로써 얻을 수 있다. Date now = new Date(); 강원대학교
GreetingRunnable의 run 메소드에서 할 일 • 1초 기다리기 위해서는 Thread 클래스의 sleep 메소드 호출 • A sleeping thread can generate an InterruptedException • Catch the exception • Terminate the thread Thread.sleep(milliseconds) 강원대학교
Generic run Method public void run() { try {Task statements } catch (InterruptedException exception) { }Clean up, if necessary} 강원대학교
File GreetingRunnable.java 01: import java.util.Date; 02: 03: /** 04: A runnable that repeatedly prints a greeting. 05: */ 06:public class GreetingRunnable implements Runnable 07: { 08: /** 09: Constructs the runnable object. 10: @param aGreeting the greeting to display 11: */ 12:public GreetingRunnable(String aGreeting) 13: { 14: greeting = aGreeting; 15: } 16: 17:public void run() 18: { 강원대학교
File GreetingRunnable.java 19:try 20: { 21:for (int i = 1; i <= REPETITIONS; i++) 22: { 23: Date now = new Date(); 24: System.out.println(now + " " + greeting); 25: Thread.sleep(DELAY); 26: } 27: } 28:catch (InterruptedException exception) 29: { 30: } 31: } 32: 33:private String greeting; 34: 35:private static final int REPETITIONS = 10; 36:private static final int DELAY = 1000; 37: } 강원대학교
File GreetingThreadTester.java 01: import java.util.Date; 02: 03: /** 04: This program tests the greeting thread by running two 05: threads in parallel. 06: */ 07:public class GreetingThreadTester 08: { 09:public static void main(String[] args) 10: { 11: GreetingRunnable r1 = new GreetingRunnable("Hello, World!"); 12: GreetingRunnable r2 = new GreetingRunnable("Goodbye, World!"); 강원대학교
File GreetingThreadTester.java 13: Thread t1 = new Thread(r1); 14: Thread t2 = new Thread(r2); 15: t1.start(); 16: t2.start(); 17: } 18: } 19: 강원대학교
Output Thu Dec 28 23:12:03 PST 2004 Hello, World! Thu Dec 28 23:12:03 PST 2004 Goodbye, World! Thu Dec 28 23:12:04 PST 2004 Hello, World! Thu Dec 28 23:12:05 PST 2004 Hello, World! Thu Dec 28 23:12:04 PST 2004 Goodbye, World! Thu Dec 28 23:12:05 PST 2004 Goodbye, World! Thu Dec 28 23:12:06 PST 2004 Hello, World! Thu Dec 28 23:12:06 PST 2004 Goodbye, World! Thu Dec 28 23:12:07 PST 2004 Hello, World! Thu Dec 28 23:12:07 PST 2004 Goodbye, World! Thu Dec 28 23:12:08 PST 2004 Hello, World! Thu Dec 28 23:12:08 PST 2004 Goodbye, World! Thu Dec 28 23:12:09 PST 2004 Hello, World! Thu Dec 28 23:12:09 PST 2004 Goodbye, World! Thu Dec 28 23:12:10 PST 2004 Hello, World! Thu Dec 28 23:12:10 PST 2004 Goodbye, World! Thu Dec 28 23:12:11 PST 2004 Goodbye, World! Thu Dec 28 23:12:11 PST 2004 Hello, World! Thu Dec 28 23:12:12 PST 2004 Goodbye, World! Thu Dec 28 23:12:12 PST 2004 Hello, World! 강원대학교
2 Runnables 2 Threads vs 1 Runnable and 2 Threads GreetingRunnable r1 = new GreetingRunnable("Hello, World!"); GreetingRunnable r2 = new GreetingRunnable("Goodbye, World!"); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); Thread t1 = new Thread(r1); Thread t2 = new Thread(r1); t1.start(); t2.start(); 강원대학교
Thread Scheduler • Thread scheduler는 각 쓰레드를 짧은 시간 (time slice) 동안 실행(activate)시킨다. • 쓰레드 실행 시간에는 작은 변이가 있을 수 있다(특히 입출력 동작시). • 쓰레드 실행 순서에는 어떤 보장도 없다. 강원대학교
Terminating Threads • 쓰레드는 run메소드가 완료되면 종료된다. • run 메소드가 완료되기 전에 쓰레드를 종료시키려면 그 쓰레드에 interrupt를 호출한다. • interrupt가 쓰레드를 강제로 종료시키는 것은 아니다.쓰레드 자료구조 내의 특정 비트를 세트할 뿐이다. 쓰레드가 이를 감지하고 스스로 종료해야 한다. t.interrupt(); 강원대학교
Terminating Threads public void run() { for (int i = 1; i <= REPETITIONS && !Thread.interrupted(); i++) {Do work } Clean up} 인터럽트가 걸렸는지 스스로 확인하는 법 강원대학교
Terminating Threads • sleep 메소드 실행 중 interrupt가 걸리면 InterruptedException이 발생됨 • Interrupt가 걸린 상태에서 sleep 메소드가 호출되는 경우에도 InterruptedException이 발생됨 • run 메소드 내에 sleep 문장이 있는 경우에는 interrupt가 걸렸는지 일일이 확인할 필요 없이 InterruptedException 발생 여부만을 감시하면 됨 강원대학교
Terminating Threads public void run() { try { for (int i = 1; i <= REPETITIONS; i++) { Do work and sleep} } catch (InterruptedException exception) { 아무것도 하지 않거나 (종료) 적절한 작업을 함 } Clean up} 강원대학교
public class MyRunnable implements Runnable { public void run() { try { System.out.println(1); Thread.sleep(1000); System.out.println(2); } catch (InterruptedException exception) { System.out.println(3); } System.out.println(4); }} Thread t = new Thread(new MyRunnable()); t.start(); t.interrupt(); 강원대학교
경쟁 조건 (Race Conditions) • 여러 쓰레드가 하나의 자료를 공유하며 자료를 업데이트 할 때 이 자료가 엉망이 될 수 있다. • 예: 여러 쓰레드가 은행계좌를 조작할 때 강원대학교
DepositRunnable의 run 메소드 WithdrawRunnable class is similar public void run() { try { for (int i = 1; i <= count; i++) { account.deposit(amount); Thread.sleep(DELAY); } } catch (InterruptedException exception) { }} 강원대학교
Sample Application • BankAccount 객체 구성 • 두 개의 쓰레드를 구성 • t1은 $100를 10번 저축함 • t2는 $100를 10번 인출함 강원대학교
Sample Application public void deposit(double amount) { System.out.print("Depositing " + amount); double newBalance = balance + amount; System.out.println(", new balance is " + newBalance); balance = newBalance; } * withraw 메소드도 유사한 형태로 구현 강원대학교
Sample Application Depositing 100.0, new balance is 100.0 Withdrawing 100.0, new balance is 0.0 Depositing 100.0, new balance is 100.0 Depositing 100.0, new balance is 200.0 Withdrawing 100.0, new balance is 100.0 . . . Withdrawing 100.0, new balance is 0.0 Depositing 100.0Withdrawing 100.0, new balance is 100.0, new balance is -100.0 강원대학교
이렇게 한다 해도 해결되지 않음 • Race condition can still occur: public void deposit(double amount) { balance = balance + amount; System.out.print("Depositing " + amount + ", new balance is " + balance); } balance = the right-hand-side value 강원대학교
File BankAccountThreadTester.java 01: /** 02: This program runs two threads that deposit and withdraw 03: money from the same bank account. 04: */ 05:public class BankAccountThreadTester 06: { 07:public static void main(String[] args) 08: { 09: BankAccount account = new BankAccount(); 10:final double AMOUNT = 100; 11:final int REPETITIONS = 1000; 12: 13: DepositRunnable d = new DepositRunnable( 14: account, AMOUNT, REPETITIONS); 15: WithdrawRunnable w = new WithdrawRunnable( 16: account, AMOUNT, REPETITIONS); 강원대학교
File BankAccountThreadTester.java 17: 18: Thread t1 = new Thread(d); 19: Thread t2 = new Thread(w); 20: 21: t1.start(); 22: t2.start(); 23: } 24: } 25: 강원대학교
File DepositRunnable.java 01: /** 02: A deposit runnable makes periodic deposits to a bank // account. 03: */ 04:public class DepositRunnable implements Runnable 05: { 06: /** 07: Constructs a deposit runnable. 08: @param anAccount the account into which to deposit // money 09: @param anAmount the amount to deposit in each //repetition 10: @param aCount the number of repetitions 11: */ 12:public DepositRunnable(BankAccount anAccount, double anAmount, 13:int aCount) 14: { 강원대학교
File DepositRunnable.java 15: account = anAccount; 16: amount = anAmount; 17: count = aCount; 18: } 19: 20:public void run() 21: { 22:try 23: { 24:for (int i = 1; i <= count; i++) 25: { 26: account.deposit(amount); 27: Thread.sleep(DELAY); 28: } 29: } 30:catch (InterruptedException exception) {} 31: } 32: 강원대학교
File DepositRunnable.java 33:private static final int DELAY = 1; 34:private BankAccount account; 35:private double amount; 36:private int count; 37: } 강원대학교
File WithdrawalRunnable.java 01: /** 02: A withdraw runnable makes periodic withdrawals from a // bank account. 03: */ 04:public class WithdrawRunnable implements Runnable 05: { 06: /** 07: Constructs a withdraw runnable. 08: @param anAccount the account from which to withdraw money 09: @param anAmount the amount to deposit in each repetition 10: @param aCount the number of repetitions 11: */ 12:public WithdrawRunnable(BankAccount anAccount, double anAmount, 13:int aCount) 14: { 15: account = anAccount; 16: amount = anAmount; 17: count = aCount; 18: } 강원대학교
File WithdrawalRunnable.java 19: 20:public void run() 21: { 22:try 23: { 24:for (int i = 1; i <= count; i++) 25: { 26: account.withdraw(amount); 27: Thread.sleep(DELAY); 28: } 29: } 30:catch (InterruptedException exception) {} 31: } 32: 33:private static final int DELAY = 1; 34:private BankAccount account; 35:private double amount; 36:private int count; 37: } 강원대학교
File BankAccount.java 01: /** 02: A bank account has a balance that can be changed by 03: deposits and withdrawals. 04: */ 05:public class BankAccount 06: { 07: /** 08: Constructs a bank account with a zero balance. 09: */ 10:public BankAccount() 11: { 12: balance = 0; 13: } 14: 15: /** 16: Deposits money into the bank account. 17: @param amount the amount to deposit 18: */ 강원대학교
File BankAccount.java 19:public void deposit(double amount) 20: { 21: System.out.print("Depositing " + amount); 22:double newBalance = balance + amount; 23: System.out.println(", new balance is " + newBalance); 24: balance = newBalance; 25: } 26: 27: /** 28: Withdraws money from the bank account. 29: @param amount the amount to withdraw 30: */ 31:public void withdraw(double amount) 32: { 33: System.out.print("Withdrawing " + amount); 34:double newBalance = balance - amount; 35: System.out.println(", new balance is " + newBalance); 36: balance = newBalance; 37: } 강원대학교
File BankAccount.java 38: 39: /** 40: Gets the current balance of the bank account. 41: @return the current balance 42: */ 43:public double getBalance() 44: { 45:return balance; 46: } 47: 48:private double balance; 49: } 강원대학교
File BankAccount.java Output Depositing 100.0, new balance is 100.0 Withdrawing 100.0, new balance is 0.0 Depositing 100.0, new balance is 100.0 Withdrawing 100.0, new balance is 0.0 . . . Withdrawing 100.0, new balance is 400.0 Depositing 100.0, new balance is 500.0 Withdrawing 100.0, new balance is 400.0 Withdrawing 100.0, new balance is 300.0 강원대학교
객체 접근 동기화(Synchronizing Object Access) • 두 개 이상이 쓰레드가 하나의 객체에 접근할 때 그 시간을 통제하여 경쟁조건을 해결하는 것 • Lock인터페이스와 Lock 인터페이스를 구현한 클래스를 이용 • ReentrantLock: 흔히 쓰이는 lock 클래스 • Lock은 Java version 5.0 이후 기능 강원대학교
Synchronizing Object Access • 통상 공유자료에 접근하는 메소드를 갖는 클래스에 lock 객체 삽입 public class BankAccount { public BankAccount() { balanceChangeLock = new ReentrantLock(); . . . } . . . private Lock balanceChangeLock;} 강원대학교
Synchronizing Object Access • 공유 자료에 접근하는 코드를 lock과 unlock으로 둘러쌈 balanceChangeLock.lock(); Code that manipulates the shared resource balanceChangeLock.unlock(); 강원대학교
Synchronizing Object Access • 쓰레드가 lock호출에 성공하면 Unlock을 호출할 때까지 lock을 점유함 • 다른 쓰레드가 lock을 점유하고 있는 동안 lock을 호출하는 쓰레드는 일시적으로 비활성화됨(deactivated) • Thread scheduler는 주기적으로 쓰레드를 활성화시켜 다시 lock을 점유할 기회를 줌 강원대학교
Synchronizing Object Access • lock과unlock사이에서 예외가 발생하면 unlock이 영영 실행되지 못함 • 이런 문제를 해결하기 위해 unlock을 finally절에 넣음 강원대학교
Synchronizing Object Access public void deposit(double amount) { balanceChangeLock.lock(); try { System.out.print("Depositing " + amount); double newBalance = balance + amount; System.out.println(", new balance is " + newBalance); balance = newBalance; } finally { balanceChangeLock.unlock(); } } * withraw 메소드도 같은 요령으로 처리 강원대학교
Deadlock(교착상태) • 쓰레드들이 서로 다른 쓰레드의 작업이 마무리 되기를 기다리고 있으나 실제로는 서로 맞물려 더이상 진행하지 못하는 상태 강원대학교
Deadlock(교착상태) 예 • 잔고가 음수로 떨어지지 않도록 하고 싶은 경우 public void withdraw(double amount) { balanceChangeLock.lock(); try { while (balance < amount) Wait for the balance to grow . . . } finally { balanceChangeLock.unlock(); }} 이 부분에서 sleep을 호출하면 lock을 계속 점유하므로 다른 쓰레드가 deposit할 수 없게 됨 – deadlock! 강원대학교