Thread 란 ?
하나의 프로세스는 하나 이상의 thread를 가지게 되고, 실제 작업을 수행하는 단위는 thread 이다.
여기서 프로세스란 프로그램이 실행되면 OS(운영체제)로부터 메모리를 할당받아서 프로세스 상태가 된다.
쓰레드를 생성하면 Runnable 상태가 된다.
Runnable상태는 CPU를 사용할 수 있는 상태인데, Run상태가 되면 CPU배분이 되어 메모리에 올라간다.
Not Runnable 상태는 ① 일정시간 쓰레드를 멈추게 하는 Thread.sleep() 메서드를 사용하면 Not Runnable 상태가 됐다가 일정시간이 지나면 다시 Runnable 상태로 돌아온다.
② 쓰레드를 잠시 기다리게 하는 wait()메서드를 사용하면 Not Runnable 상태가 되는데,
이때 깨워주는 메서드는 notify()나 notifyAll()을 사용하면 된다.
③ 마지막은 join()을 사용하게 되면 join()을 호출한 다른 쓰레드가 끝날 때까지 내 쓰레드가 Not Runnable 상태가 된다.
- 멀티 쓰레드
: 여러 쓰레드가 수행되는 프로그래밍, 여러 작업이 동시에 실행되는 효과를 얻을 수 있다.
- 멀티 쓰레드 프로그래밍의 동기화
두 개 이상의 쓰레드가 같은 객체에 접근할 경우, 동시에 접근함으로써 오류가 발생한다.
이렇게 여러 쓰레드가 공유하는 자원 중 경쟁이 발생하는 부분을 임계영역이라고 하는데,
동기화는 임계영역에 접근한 경우 공유자원을 lock하여 다른 쓰레드의 접근을 제어한다.
키워드는 'synchronized'를 사용
- 동기화(synchronized)를 사용한 쓰레드 예제
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 | package ch02; public class BankAccount { int money = 100_000; public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } // 출금, 입금 // synchronized <-- 동기화 처리 진행 public synchronized void saveMoney(int money) { int currentMoney = getMoney(); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } setMoney(currentMoney + money); System.out.println("입금 후 계좌 잔액 : " + getMoney()); } public int withdraw(int money) { // 10만원 상태 synchronized (this) { int currentMoney = getMoney(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (currentMoney >= money) { setMoney(currentMoney - money); System.out.println("출금 후 계좌 잔액 : " + getMoney()); return money; } else { System.out.println("잘못된 입력입니다."); return 0; } } } } class Father extends Thread { BankAccount account; public Father(BankAccount account) { this.account = account; } @Override public void run() { // 입금 시키기 account.saveMoney(10_000); } } class Mother extends Thread { BankAccount account; public Mother(BankAccount account) { this.account = account; } @Override public void run() { // 출금 시키기 account.withdraw(5_000); } } | cs |
→ get, set 메서드를 이용하여 출금, 입금 메서드를 만들어주었다.
→ saveMoney 메서드 :
getMoney()를 currentMoney(현재 갖고 있는 돈)에 저장한다.
입금하는데 걸리는 시간은 3초라 가정하고 Thread.sleep(3000)을 해주었다.
setMoney에 currentMoney와 money를 더한 값을 저장해주고
입금후 계좌 잔액을 getMoney()메서드를 통해 출력하였다.
→ withdraw 메서드 :
synchronized(this) 를 사용하여 묶어주어 동기화를 사용할 수도 있다.
getMoney()를 currentMoney(현재 갖고 있는 돈)에 저장한다.
출금하는데 걸리는 시간이 0.5초라 가정하고 Thread.sleep(500)을 해주었다.
출금하려면 현재 가지고 있는 돈이 더 많아야 하므로 currentMoney>=money로 조건식을 써주고
setMoney에 currentMoney - money를 저장하고 출금 후 계좌 잔액을 getMoney()를 통해 출력하고 money를 반환하였다.
그리고 현재 가지고 있는 돈이 출금하려는 돈보다 적으면 잘못된 입력입니다. 라고 출력하고 0을 반환하도록 하였다.
→ Father 클래스, Mother 클래스 :
Father, Mother 클래스가 Thread를 상속받고 있으므로 BankAccount를 상속받지 못하니 멤버변수로 BankAccount를 선언하여 생성자에서 참조하여 메모리에 올려주도록 설계하였다.
아버지는 10_000 입금, 어머니는 5_000 출금하도록 하였다.
MainTest1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package ch02; public class MainTest1 { public static void main(String[] args) { // 현재 잔액 100_000원 BankAccount b = new BankAccount(); Father f = new Father(b); Mother m = new Mother(b); //아버지 입금하기 f.start(); //3초 걸림 //어머니 출금하기 m.start(); } } | cs |
→ BankAccount 객체를 생성하고 Father 클래스, Mother 클래스도 객체를 생성하여 BankAccount를 메모리에 올리기 위해 b를 참조한다.
<결과 화면>
→ 정상 처리(동기화 처리) : 10만원 + 1만원 - 5천원 == 10만 5천원
→ 동기화(synchronized)를 사용하지 않았을 때
두 개 이상의 쓰레드가 같은 객체에 접근하려 함으로써 발생하는 오류
'프로그래밍 > Java' 카테고리의 다른 글
Java_Input/Output 스트림(Stream) (0) | 2023.03.02 |
---|---|
Java_컬렉션 프레임워크 (0) | 2023.02.21 |
Java_Swing 키보드 방향키로 이미지를 창 안에서만 움직이게 하기 (0) | 2023.02.16 |
Java_예외처리 (0) | 2023.02.16 |
Java_추상 클래스(abstract class) (0) | 2023.02.14 |