스레드(Thread)
멀티스레드와 멀티태스킹 차이 이해
자바 가상 머신이 제공하는 스레드 모델 이해
자바 스레드 구현법 실습
스레드 스케줄링 학습
학습목표
스레드 스케줄링 학습
스레드 생명 주기 이해
자바 스레드의 동기화와 통신 메커니즘 이해
스레드
하나의 프로그램에서 동시에 하나 이상이 실행
메모리 자원을 절약할 수 있어 효율적
cf. 멀티태스킹여러 개의 프로그램이 동시에 실행
자바 언어는 스레드를 기본 패키지와 키워드에서 제공
스레드와 멀티스레드
자바 언어는 스레드를 기본 패키지와 키워드에서 제공
멀티스레드
하나의 프로그램 내부에서 마치 두 개 이상의 프로그램 이 동작하는 것과 같은 기능
병행 처리라고도 함.
스레드와 멀티스레드
병행 처리
한 개의 CPU가 동시에 실행 주기를 수행하는 것
병렬 처리
두개 이상의 CPU가 동시에 실행 주기를 수행하는 것
자바 스레드 관련 예제
public class Thread00 {
public static void main(String[] args) { // 스레드를 생성한다.
Thread thread = new Thread();
// 스레드를 시작한다.
thread.start();
} } }
아무런 내용이 표시되지 않는다.
결과
자바 스레드 관련 예제
public class Thread01 extends Thread { public void run()
{
for (int i = 0; i < 5; i++)
System.out.println("Thread 1:" + i);
}
public static void main(String[] args) { Thread 0:0 결과
Thread 클래스를
상속해서 스레드 구현
public static void main(String[] args) { Thread thread = new Thread01();
thread.start();
for (int i = 0; i < 5; i++)
System.out.println("Thread 0:" + i);
} }
Thread 0:0 Thread 0:1 Thread 0:2 Thread 0:3 Thread 0:4 Thread 1:0 Thread 1:1 Thread 1:2 Thread 1:3 Thread 1:4
자바 스레드 관련 예제
public class Thread02 extends Thread { public void run()
{
for (int i = 0; i < 5; i++)
System.out.println("Thread 1:" + i);
}
Thread 1:0 결과
run() 메소드 사용 예제
public static void main(String[] args) { Thread thread = new Thread02();
thread.run();
for (int i = 0; i < 5; i++)
System.out.println("Thread 0:" + i);
} }
Thread 1:0 Thread 1:1 Thread 1:2 Thread 1:3 Thread 1:4 Thread 0:0 Thread 0:1 Thread 0:2 Thread 0:3 Thread 0:4
자바 스레드 관련 예제
public class Thread03 extends Thread { public void run()
{
// sleep() 메소드는 try ... catch 예외 처리를 필요로 한다.
try {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 1:" + i);
// 1초간 정지한다.
sleep(1000);
sleep() 메소드 사용 예제
sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
} }
public static void main(String[] args) { Thread thread = new Thread03();
thread.start();
자바 스레드 관련 예제
// sleep() 메소드는 try ... catch 예외 처리를 필요로 한다.
try {
for (int i = 0; i < 5; i++) {
System.out.println("Thread 0:" + i);
// 1초간 정지한다.
Thread.sleep(1000);
}
} catch (Exception e) {
Thread 0:0 Thread 1:0 Thread 0:1 Thread 1:1 Thread 0:2
결과
{
e.printStackTrace();
} } }
Thread 0:2 Thread 1:2 Thread 0:3 Thread 1:3 Thread 0:4 Thread 1:4
자바 스레드 관련 예제
public class Thread04 implements Runnable { int i;
public Thread04() {
i = 0;
}
public void run()
Runnable 인터페이스 구현
public void run() {
// sleep() 메소드는 try ... catch 예외 처리를 필요로 한다.
try {
while(i < 10) {
// Thread.currentThread()는 현재의 스레드를 얻는 메소드 // obj.getName()은 스레드의 이름을 얻는 메소드
System.out.println(Thread.currentThread().getName() + ":" + i);
i++;
자바 스레드 관련 예제
// 1초간 정지한다.
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
} Thread-0:0
결과 }
public static void main(String[] args) { Thread04 ex = new Thread04();
Thread thread0 = new Thread(ex);
thread0.start();
Thread thread1 = new Thread(ex);
thread1.start();
}
Thread-0:0 Thread-1:1 Thread-0:2 Thread-1:3 Thread-0:4 Thread-1:5 Thread-0:6 Thread-1:7 Thread-0:8 Thread-1:9
자바 스레드 관련 예제
public class Thread05 implements Runnable { int i;
public Thread05() {
i = 0;
}
public void run()
스레드 이름을 정의할 수 있도록 수정한 예제
public void run() {
String name;
// sleep() 메소드는 try ... catch 예외 처리를 필요로 한다.
try {
while(i < 10) {
// Thread.currentThread()는 현재의 스레드를 얻는 메소드 Thread thread = Thread.currentThread();
// thread.getName()은 스레드의 이름을 얻는 메소드 name = thread.getName();
System.out.println(name + ":" + i);
i++;
자바 스레드 관련 예제
// 1초간 정지한다.
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
} 스레드0:0
결과
}
public static void main(String[] args) { Thread05 ex = new Thread05();
Thread thread0 = new Thread(ex, "스레드0");
thread0.start();
Thread thread1 = new Thread(ex, "스레드1");
thread1.start();
}
스레드0:0 스레드1:1 스레드0:2 스레드1:3 스레드0:4 스레드1:5 스레드0:6 스레드1:7 스레드0:8 스레드1:9
스레드 생명 주기
born
Thread 생성
start() 메소드 호출
sleep() 메소드 호출 wait() 메소드 호출
ready blocked
dead
wait() 메소드 호출 I/O 요청
sleep 시간 완료 notify() 메소드 호출
I/O 완료 run() 메소드 완료
스레드 생명주기: Ready 상태와 Blocked 상태의 전환
ready 상태와 blocked 상태의 상호 전환 사유
ready 상태 -> blocked 상태 blocked 상태 -> ready 상태
객체의 wait() 메소드 호출 객체의 notify()/notifyAll() 메소드 호출 sleep() 메소드 호출 sleep() 상태가 종료되었을 경우
I/O 동작 수행 I/O 동작 완료
다른 스레드의 join() 메소드 호출
suspend() 메소드 호출(권장되지 않음) resume() 메소드 호출(권장되지 않음)
스레드 생명주기 관련 예제
public class Thread06 implements Runnable { public void run()
{
// 스레드로 동작 시작
System.out.println("Thread1 started.");
try {
// 5초간sleep
Thread.sleep(5000);
} catch (Exception e)
Thread1 created.
Thread1 started.
Thread1 ended.
Thread1 joined.
결과
join() 메소드 이용 예제
// 스레드실행
thread.start();
} catch (Exception e) {
e.printStackTrace();
}
// 스레드종료 알림
System.out.println("Thread1 ended.");
}
public static void main(String[] args) { // Thread06 객체를
// Runnable 참조 변수로 저장 Runnable r = new Thread06();
// 스레드생성
Thread thread = new Thread(r);
thread.start();
// 스레드생성을 알림.
System.out.println("Thread1 created.");
try {
// 스레드 종료 시점까지 대기 thread.join();
System.out.println("Thread1 joined.");
} catch (Exception e) {
e.printStackTrace();
} } }
스레드 생명주기 관련 예제
스레드 생성과 Join() 메소드
start()
쓰레드 생성 새 쓰레드
main() 쓰레드
start()
run() join()
쓰레드 종료 다음 실행
종료 대기 시간 t
스레드 생명주기 관련 예제
public class Thread07 implements Runnable { public void run()
{
// 스레드로 동작 시작
System.out.println("Thread1 started.");
try {
while(true) {
if (Thread.interrupted() == true) { System.out.println("Thread1 interrupted.");
interrupt() 메소드 이용 예제
System.out.println("Thread1 interrupted.");
break;
} }
} catch (Exception e) {
e.printStackTrace();
}
// 스레드종료 알림
System.out.println("Thread1 ended.");
}
public static void main(String[] args) { // Thread07 객체를 Runnable 참조 변수로 저장 Runnable r = new Thread07();
스레드 생명주기 관련 예제
// 스레드생성 결과
Thread thread1 = new Thread(r);
// 스레드실행
thread1.start();
// 스레드생성을 알림.
System.out.println("Thread1 created.");
try {
// 1초간 대기
Thread.sleep(1000);
// Thread1의 인터럽트 여부 확인
Thread1 created.
Thread1 started.
interrupted() before interrupt() calling: false interrupted() after interrupt() calling: true Thread1 interrupted.
Thread1 ended.
Thread1 joined.
// Thread1의 인터럽트 여부 확인
System.out.println("Thread1.interrupted() before interrupt() calling: " + thread1.isInterrupted());
// Thread1의interrupt() 메소드 호출 thread1.interrupt();
// Thread1의 인터럽트 여부 확인
System.out.println("Thread1.interrupted() after interrupt() calling: " + thread1.isInterrupted());
// 스레드 종료 시점까지 대기 thread1.join();
System.out.println("thread1 joined.");
} catch (Exception e) {
e.printStackTrace();
} }
스레드 생명주기 관련 예제
예제 Thread11.java 동작 다이어그램
쓰레드1 생성 main() 쓰레드
쓰레드1 생성 쓰레드1 start()
쓰레드1
쓰레드2
쓰레드2 start()
쓰레드 종료 yield()
run()
run()
쓰레드 종료 yeield()
스레드 생명주기 관련 예제
public class Thread11 implements Runnable { public void run()
{
// 현재 스레드를 구하여 thread 변수에 저장 Thread thread = Thread.currentThread();
// 현재 스레드의 이름을 구하여 지역 변수 name에 저장.
String name = thread.getName();
yield() 메소드 이용 예제
String name = thread.getName();
int j = 5;
try {
// 총 5번의 반복 while(j-->0) {
i += i;
System.out.println(name + ": " + i);
// 다른스레드를 실행 Thread.yield();
}
} catch (Exception e)
스레드 생명주기 관련 예제 EX14_11.java
{
e.printStackTrace();
} }
private int i = 1;
public static void main(String[] args) { Runnable r = new Thread11();
결과 Thread1: 2 Thread2: 4 Thread1: 8 Thread2: 16 Thread1: 32 Runnable r = new Thread11();
// 2개의 스레드 생성
Thread thread1 = new Thread(r, "Thread1");
Thread thread2 = new Thread(r, "Thread2");
// 2개의 스레드 시작 thread1.start();
thread2.start();
} }
Thread1: 32 Thread2: 64 Thread1: 128 Thread2: 256 Thread1: 512 Thread2: 1024
스레드 우선순위
스레드 우선순위
getPriority() 메소드 : 현재 스레드 우선순위를 얻는 메 소드
setPriority() 메소드: 현재의 스레드 우선순위 결정
우선순위는 int 정수형
우선순위 관련 상수
Thread.MIN_PRIORITY
Thread.NORM_PRIORITY
Thread.MAX_PRIORITY
스레드 우선순위 관련 예제
public class Thread08 extends Thread {
public void run() {
System.out.println("Thread.MIN_PRIORITY: " + MIN_PRIORITY);
System.out.println("Thread.NORM_PRIORITY: " + NORM_PRIORITY);
System.out.println("Thread.MAX_PRIORITY: " + MAX_PRIORITY);
try {
// 스레드 시작을 출력
System.out.println("thread1 started.");
// 시작 시점의 우선순위 출력
System.out.println("thread1 priority at start: " + getPriority());
우선순위 지정
우선순위 상수 사용 예제
System.out.println("thread1 priority at start: " + getPriority());
// 잠시 0.5초간 sleep sleep(500);
// 우선순위 2로 수정 setPriority(2);
// 현재의 우선순위 출력
System.out.println("thread1 priority in running: " + getPriority());
// 스레드 종료 확인
System.out.println("thread1 ended.");
} catch (Exception e) {
e.printStackTrace();
} }
public static void main(String[] args) { Thread thread0 = Thread.currentThread();
Thread thread1 = new Thread08();
스레드 우선순위 관련 예제 EX14_08.java
System.out.println("thread0 priority: " + thread0.getPriority());
System.out.println("thread1 priority: " + thread1.getPriority());
System.out.println("thread1 isAlive() before start(): " + thread1.isAlive());
thread1.start();
// 스레드1의 우선순위를 1로 수정 thread1.setPriority(1);
try {
// 0.1초간sleep Thread.sleep(100);
// 스레드1이 종료했는지 안 했는지 확인
System.out.println("thread1 isAlive() System.out.println("thread1 isAlive() after start(): " + thread1.isAlive());
// 1초간sleep
Thread.sleep(1000);
// 스레드1의 종료를 기다림 thread1.join();
// 스레드1이 종료했는지를 확인
System.out.println("thread1 isAlive() after joining: " + thread1.isAlive());
// 스레드1의 우선순위를 3으로 수정 thread1.setPriority(3);
} catch (Exception e) {
e.printStackTrace();
} }
결과
thread0 priority: 5 thread1 priority: 5
thread1 isAlive() before start(): false Thread.MIN_PRIORITY: 1
Thread.NORM_PRIORITY: 5 Thread.MAX_PRIORITY: 10 thread1 started.
thread1 priority at start: 1
thread1 isAlive() after start(): true thread1 priority in running: 2 thread1 ended.
thread1 isAlive() after joining: false java.lang.NullPointerException
at java.lang.Thread.setPriority(Unknown Source)
스레드 스케줄링
스레드는 우선 순위 이외에도 데몬 스레드인지 아닌지 판단하는 플래그 속성을 갖고 있음
데몬 스레드
데몬 스레드
서비스 스레드라고도 함.
낮은 우선순위를 갖는 경우가 많음.
대표적인 데몬 스레드는 가비지 컬렉터
스레드 우선순위 관련 예제
public class Thread09 implements Runnable { public void run()
{
Thread.currentThread().setDaemon(false);
}
public static void main(String[] args) { Runnable r = new Thread09();
Thread thread1 = new Thread(r, "EX14_09");
System.out.println("thread1.isDaemon() before setDaemon(): " +
setDaemon() 메소드 사용 예제
System.out.println("thread1.isDaemon() before setDaemon(): " + thread1.isDaemon());
thread1.setDaemon(true);
System.out.println("thread1.isDaemon() after setDaemon(true): " + thread1.isDaemon());
thread1.start();
System.out.println("thread1.isDaemon() after start(): " + thread1.isDaemon());
} thread1.isDaemon() before setDaemon(): false
thread1.isDaemon() after setDaemon(true): true thread1.isDaemon() after start(): true
java.lang.IllegalThreadStateException
at java.lang.Thread.setDaemon(Unknown Source) at Thread09.run(Thread09.java:16)
결과
안전한 스레드 중지 관련 예제
public class Thread10 extends Thread { private boolean stop = false;
public void run() {
// 스레드가 동작했음을 출력
System.out.println("Thread1 is running.");
// stop이 true일때까지 무한 반복 while(!stop)
중지 플래그를 이용한 예제
while(!stop) {
// 여기에 코드를 삽입함 }
System.out.println("Thread1 is dead.");
}
public void setStop() {
stop = true;
}
public static void main(String[] args) { Thread10 thread1 = new Thread10();
안전한 스레드 중지 관련 예제 EX14_10.java
try {
// 스레드를 동작시킴 thread1.start();
System.out.println("Thread1 started.");
// .5초간sleep
Thread.sleep(500);
// setStop() 메소드 호출 thread1.setStop();
Thread1 started.
Thread1 is running.
Thread1.setStop() called.
Thread1 is dead.
Thread1 joined.
결과
thread1.setStop();
System.out.println("Thread1.setStop() called.");
// 스레드 종료를 기다림 thread1.join();
System.out.println("Thread1 joined.");
} catch (Exception e) {
e.printStackTrace();
} }
}
스레드 관리
스레드 관리
지역 변수를 제외한 모든 데이터와 메소드 공유 가능
다만 공유할 경우, 공유 변수를 여러 스레드 사이에서 단 하나의 스레드에게만 사용 권한을 주는 것이 안전 단 하나의 스레드에게만 사용 권한을 주는 것이 안전
원자 코드
다른 스레드에 의해서 인터럽트될 수 없는 코드
키워드 synchronized
자바 언어에서 원자 코드와 유사한 기능 수행
스레드 관리 관련 예제
public class Thread13 implements Runnable { int money_in_bank = 100000000;
public void run() {
int money_need = 60000000;
int money = 0;
String msg = null;
try {
키워드 Synchronized를 이용한 예제
try {
synchronized (this) {
if (getMoney() > money_need) { Thread.yield();
money = drawMoney(money_need);
msg = "인출 성공";
} else {
money = 0;
msg = "인출 실패";
} }
System.out.println(msg + ", 인출금액: " + money + ", 잔고: " + getMoney());
스레드 관리 관련 예제
} catch (Exception e) {
e.printStackTrace();
} }
private int getMoney() {
return money_in_bank;
}
private int drawMoney(int m) private int drawMoney(int m) {
money_in_bank -= m;
return m;
}
public static void main(String[] args) { Runnable r = new Thread13();
Thread thread1 = new Thread(r);
Thread thread2 = new Thread(r);
thread1.start();
thread2.start();
} }
인출 실패, 인출금액: 0, 잔고: 40000000
인출 성공, 인출금액: 60000000, 잔고: 40000000 결과