Java Day 23 스레드
06 Jan 2021 | JavaJava Day 23 스레드
-
Thread
- Multi-Tasking
- 프로세스(Process)란 운영체제에서 실행중인 하나의 프로그램
- 멀티 프로세스(Multi-Process) : 두 개 이상의 프로세스가 실행되는 것
- 멀티 태스킹(Multi-Tasking) : 두 개 이상의 프로세스를 실행하여 일을 처리하는 것
- Multi-Thread
- 스레드(Thread)란 프로세스 내에서 실행되는 세부 작업 단위
- 멀티 스레드(Multi-Thread)란 하나의 프로세스에서 여러 개의 스레드가 병행처리 되는 것
- 멀티 스레드로 동작하는 스레드의 예외는 다른 스레드나 프로세스 자체에 영향을 끼칠 우려가 있기 때문에 예외 처리를 잘 해야한다
- Life cycle of a Thread
- 스레드는 Thread 객체가 생성되면 생명주기를 갖게 된다
- New - 스레드가 생성된 상태
- Runnable - 객체 생성 후 start() 메소드 호출시 Runnanle 상태로 이동
- Running - Runnable 상태에서 스레드 스케쥴러에 의해 Running 상태로 이동
- Blocked - 스레드가 다른 특정한 이유로 Running에서 Blocked 상탤로 이동
- Dead - 스레드가 종료되면 그 스레드는 다시 시작 할 수 없게 된다
- Thread 생성 방법
- Thread를 상속 받아서 클래슬 만들기
- Runnable 인터페이스를 상속받아서 클래스 만들기
- Multi-Tasking
-
Thread Class 생성자와 메소드
- 생성자
- 메소드
-
Thread 구현
- Thread class 상속받아서 구현
// Thread를 상속받아 나 자신이 thread가 됨 public class SingleThreadEx extends Thread { private int[] temp; public SingleThreadEx(String threadName) { super(threadName); temp = new int[10]; for(int start = 0; start<temp.length; start++) { temp[start] = start; } } public void run() { for(int start : temp) { try { sleep(1000); } catch(InterruptedException ie) { ie.printStackTrace(); } System.out.printf("스레드 이름 : %s ,", currentThread().getName());; System.out.printf("temp value : %d %n", start); } } public static void main(String[] args) { SingleThreadEx st = new SingleThreadEx("첫번째"); st.start(); } }
- Runnable interface 상속받아서 구현
// Runnable이기 때문에 Thread 객체를 만들어 그 안에 넣어 사용해야함 public class SingleRunnableEx implements Runnable { private int temp[]; public SingleRunnableEx() { temp = new int[10]; for(int start=0; start<10; start++) { temp[start] = start; } } @Override public void run() { for(int start : temp) { try { Thread.sleep(1000); } catch(InterruptedException ie) { ie.printStackTrace(); } System.out.printf("스레드 이름 : %s,", Thread.currentThread().getName()); System.out.printf("temp value : %d %n", start); } } public static void main(String[] args) { SingleRunnableEx srt = new SingleRunnableEx(); Thread t = new Thread(srt, "첫번째"); t.start(); } }
-
join() 메소드
- join() 메소드는 join() 메소드를 호출한 스레드가 종료할 때까지 현재의 스레드가 기다리게 해준다.
class MyRunnable implements Runnable { @Override public void run() { System.out.println("Run"); first(); } public void first() { System.out.println("First"); second(); } public void second() { System.out.println("Second"); } } public class JoinEx { public static void main(String[] args) { System.out.println(Thread.currentThread().getName()+" start!"); Runnable r = new MyRunnable(); Thread myThread = new Thread(r); myThread.start(); try { myThread.join(); // main이 끝나기 전 start() 실행 } catch(InterruptedException ie) { ie.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" end!"); } } //main start!와 main end!의 사이에 Run, First, Second가 찍혀있다
-
Thread의 종류
- User Thread : main의 종료와 상관없이 thread를 계속 실행, setDaemon(false)
- Daemon Thread : main 종료시 thread도 같이 종료, setDaemon(true)
- setDaemon() 메소드는 반드시 start() 이전에 설정해야 한다
-
Thread 스케쥴링
- 우선순위 방식 : 우선순위가 높은 스레드가 실행 상태를 더 많이 가짐
- 순환할당 방식 : 시간 할당량을 정해서 정해진 시간만큼 실행 후 다른 스레드를 실행
- 순환할당 방식은 JVM에 의해 정해지기 때문에 코드로 제어 할 수 없다
- 우선순위 방식은 setPriority(1~10) 메소드로 1~10 까지의 우선순위를 결정 할 수 있다
- setPriority() 메소드는 반드시 start() 이전에 설정해야 한다
class CalcThread extends Thread { public CalcThread(String name) { setName(name); } public void run() { for(int i=0; i<1000000; i++) { } System.out.println(getName()); } } public class PriorityEx { public static void main(String[] args) { for(int i=1; i<=10; i++) { Thread thread = new CalcThread("Thread" + i); if (i != 10) { thread.setPriority(Thread.MIN_PRIORITY); } else { thread.setPriority(Thread.MAX_PRIORITY); } thread.start(); } } }// 우선순위가 높은 Thread10이 가장 먼저 찍힌다
-
동기화 (synchronized)
- 임계 영역 : 멀티 스레드상에서 하나의 스레드만 실행 할 수 있는 코드 영역
- 상호 배제 : 특정 프로세스가 공유 자원을 사용하고 있을때, 다른 프로세스의 접근을 제어하는 기법, 번갈아가며 쓰도록 임계 영역을 유지하는 기법
- 동기화 메소드와 블록으로 메소드 혹은 블록에 lock을 걸어 다른 스레드가 임계 영역에 들어오지 못하게 막을 수 있다
- 메소드에 sychhronized 지정어를 걸거나 메소드 안에서 sychhronized 블록을 생성하여 동기화 할 수 있다
- 동기화된 스레드는 wait(), notify(), notifyAll() 메소드로 스레드간 통신을 할 수 있다 (synchronized 블록에서만 사용 가능)
class ATM implements Runnable { private long depositMoney = 10000; public void run() { synchronized(this) { // 동기화 for(int i=0; i<10; i++) { // 임계 영역 if(getDepositMoney() <= 0) break; withDraw(1000); if(getDepositMoney() == 2000 || getDepositMoney() == 4000 || getDepositMoney() == 6000 || getDepositMoney() == 8000) { try { this.wait(); } catch(InterruptedException ie) { ie.printStackTrace(); } } else { this.notify(); } } } } public void withDraw(long howMuch) { if(getDepositMoney() > 0) { depositMoney -= howMuch; System.out.println(Thread.currentThread().getName()+", "); System.out.printf("잔액 : %,d 원 %n", getDepositMoney()); } else { System.out.println(Thread.currentThread().getName()+", "); System.out.println("잔액이 부족합니다."); } } public long getDepositMoney() { return depositMoney; } } public class SynchronizedEx { public static void main(String[] args) { ATM atm = new ATM(); Thread mother = new Thread(atm, "mother"); Thread son = new Thread(atm, "son"); mother.start(); son.start(); } } /* 출력 결과 잔액 : 9,000 원 mother, 잔액 : 8,000 원 son, 잔액 : 7,000 원 son, 잔액 : 6,000 원 mother, 잔액 : 5,000 원 mother, 잔액 : 4,000 원 son, 잔액 : 3,000 원 son, 잔액 : 2,000 원 mother, 잔액 : 1,000 원 mother, 잔액 : 0 원 */
참고 자료
KG아이티뱅크 자바 강의자료