25장 - 쓰레드는 개발자라면 알아두는 것이 좋아요
| 단일 쓰레드(Thread) java라는 명령어 실행 시 결과 출력하고 끝냄 |
➡ | 한 번 실행해 놓고, 계속 기능들 제공하는 프로그램 ex) JSP, Servlet |
프로세스가 시작되는 과정
- 자바 프로그램 사용해
- 뒤에 클래스 이름 붙이고
- 엔터 치면
- 적어도 하나의 JVM 시작됨
- 보통 이렇게 JVM 시작되면 자바 프로세스(Proceses)가 시작됨
- 이 프로세스라는 울타리 안에서 여러 개의 쓰레드가 실행됨
- 위의 순서로 하나의 프로세스 내에 여러 쓰레드가 수행됨
- But 여러 프로세스가 공유하는 하나의 쓰레드가 수행되는 일은 없음

- java 명령어 사용해 클래스 실행시키면
- 자바 프로세스 시작
- main() 메서드 수행되며
- 하나의 쓰레드가 시작됨
- 만약 많은 쓰레드 필요 시, 이 main() 메서드에 쓰레드 생성해주면 됨
- 자바 사용해 웹 제공 시에는 Tomcat 같은 WAS (Web Application Server) 사용
- 이때의 WAS도 main() 메서드에서 생성한 쓰레드들이 수행
- 아무런 쓰레드 생성하지 않아도 JVM 관리하기 위한 여러 쓰레드가 존재함
- ex) 자바의 쓰레기 객체 청소하는 GC 관련 쓰레드
쓰레드가 필요한 이유
- 프로세스가 하나 시작하려면 많은 자원(resource) 필요
- 하나의 작업 동시 수행 시 여러 개의 프로세스 띄워 실행하면 각각 메모리 할당해줘야 함
- JVM은 기본적으로 아무 옵션 없이 실행하면 OS마다 다르나, 적어도 32 ~ 64MB의 물리 메모리 점유함
- But 쓰레드 하나 추가 시 1MB 이내의 메모리 점유함
- ➡ 쓰레드 = 경량 프로세스(lightweight process)
- 요즘은 두 개 이상의 코어가 있는 멀티 코어 시대 ➡ 단일 쓰레드보단 다중 쓰레드로 실행하는 작업이 더 빠른 시간에 결과 제공해줌
Thread
실타래
쓰레드 하나 생성 시 하나의 실 줄기 추가됨
해당 쓰레드 종료 시 그 실이 끊어지는 것
쓰레드 생성 방법
| Runnable 인터페이스 | Thread 클래스 |
| Runnable 인터페이스를 구현한 클래스 | |
| java.lang 패키지에 있으므로 별도의 import 필요 x | |
Runnable 인터페이스에 선언된 유일한 메서드
| 리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
| void | run() | 쓰레드 시작되면 수행되는 메서드 |
package e.thread;
class RunnableSample implements Runnable {
public void run() {
System.out.println("This is RunnableSample's run() method.");
}
}
class ThreadSample extends Thread {
public void run() {
System.out.println("This is ThreadSample's run() method.");
}
}
package e.thread;
public class RunThreads {
public static void main(String[] args) {
RunThreads threads = new RunThreads();
threads.runBasic();
}
public void runBasic() {
RunnableSample runnable = new RunnableSample();
new Thread(runnable).start();
ThreadSample thread = new ThreadSample();
thread.start();
System.out.println("RunThreads.runBasic() method is ended.");
}
}
- run(): 쓰레드가 수행되는 우리가 구현하는 메서드
- start(): 쓰레드 시작하는 메서드
- = Runnable 인터페이스 구현 or Thread 클래스 확장 시 run() 메서드를 시작점으로 작성해야
- But 쓰레드 시작하는 메서드는 run()이 아닌 start()
- start() 메서드 만들지 않아도, 자바가 알아서 run() 메서드 수행함
new Thread(runnable).start();
thread.start();
- Thread 클래스의 생성자에 해당 객체 추가하여 시작해야 함
- 이후 start() 호출 시 쓰레드 시작
쓰레드 클래스가 다른 클래스 확장할 필요 있을 경우
- Runnable 인터페이스 구현
- 인터페이스는 다중 구현해도 문제 x
- 클래스는 자바에서 다중 상속 불가함

쓰레드에서의 순서
쓰레드 결과는 순차적으로(sequential) 실행되지 않음
쓰레드를 start() 통해 시작 = 프로세스가 아닌 하나의 쓰레드를 JVM에 추가해 실행
runBasic() 쓰레드 기동시키는 메서드에서 runnable 객체를 Thread 클래스의 start() 메서드로 시작
이때 시작한 start() 메서드가 끝날 때까지 기다리지 않고, 그 다음 줄에 있는 thread라는 객체의 start() 메서드 실행

package e.thread;
public class RunMultiThreads {
pubilc static void main(String[] args) {
RunMultiThreads sample = new RunMultiThreads();
sample.runMultiThread();
}
public void runMultiThread() {
RunnableSample[] runnable = new RunnableSample[5];
ThreadSample[] thread = new ThreadSample[5];
for(int loop = 0; loop < 5; loop++) {
runnable[loop] = new RunnableSample();
thread[loop] = new ThreadSample();
new Thread(runnable[loop]).start();
thread[loop].start();
}
System.out.println("RunMultiThreads.runMultiThread() method is ended.");
}
}
//결과
//This is RunnableSample's run() method.
//This is RunnableSample's run() method.
//This is RunnableSample's run() method.
//This is ThreadSample's run() method.
//RunMultiThreads.runMultiThread() method is ended.
//This is ThreadSample's run() method.
//This is ThreadSample's run() method.
//This is RunnableSample's run() method.
//This is ThreadSample's run() method.
//This is RunnableSample's run() method.
//This is ThreadSample's run() method.
- 새로 생성한 쓰레드는 run() 메서드 종료 시 끝남
- run() 끝나지 않으면 실행한 애플리케이션은 끝나지 않음
Thread 클래스의 생성자
| 생성자 | 설명 |
| Thread() | 새로운 쓰레드 생성 |
| Thread(Runnable target) | 매개 변수로 받은 target 객체의 run() 메서드 수행하는 쓰레드 생성 |
| Thread(Runnable target, String name) | 매개 변수로 받은 target 객체의 run() 메서드 수행 후 name이라는 이름 갖는 쓰레드 생성 |
| Thread(String name) | name이라는 이름 갖는 쓰레드 생성 |
| Thread(ThreadGroup group, Runnable target) | 매개 변수로 받은 group의 쓰레드 그룹에 속하는 target 객체의 run() 메서드 수행하는 쓰레드 생성 |
| Thread(ThreadGroup group, Runnable target, String name) | 매개 변수로 받은 group의 쓰레드 그룹에 속하는 target 객체의 run() 메서드 수행 후, name이라는 이름 갖는 쓰레드 생성 |
| Thread(ThreadGroup group, Runnable target, String name, long stackSize) | 매개 변수로 받은 group의 쓰레드 그룹에 속하는 target 객체의 run() 메서드 수행 후, name이라는 이름 갖는 쓰레드 생성 해당 쓰레드의 스택 크기는 stackSize 만큼만 가능 |
| Thread(ThreadGroup group, Stringn name) | 매개 변수로 받은 group의 쓰레드 그룹에 속하는 name이라는 이름 갖는 쓰레드 생성 |
쓰레드의 이름
- 모든 쓰레드는 이름 있음
- 아무 이름도 지정하지 않으면 그 쓰레드의 이름은 "Thread-n"
- 이때의 n = 쓰레드 생성 순서에 따라 증가
- 쓰레드 이름 지정 시 해당 쓰레드는 별도의 이름 갖게 됨(쓰레드 이름이 겹쳐도 예외나 에러 발생 x)
ThreadGroup
- 어떤 쓰레드 생성 시 ThreadGroup 통해 쓰레드 묶어놓을 수 있음
- 쓰레드의 그룹을 묶으면 ThreadGroup 클래스에서 제공하는 메서드들 통해 각종 정보 얻을 수 있음
stackSize
- 스택의 크기 의미
- 쓰레드에서 얼마나 많은 메서드 호출하는지, 얼마나 많은 쓰레드가 동시에 처리되는지는 JVM이 실행되는 OS 플랫폼에 따라 매우 다름(경우에 따라 이 값이 무시될 수도 있음)
- 여기서의 스택 != Collection의 Stack
- 자바 프로세스 시작 시 실행 데이터 공간(Runtime data area) 구성되는데, 그 중 하나가 스택이라는 공간이며, 쓰레드 생성될 때마다 별도의 스택이 할당됨
Thread 클래스 확장해 구현 시, 생성자 호출하는 방법
package e.thread;
public class NameThread extends Thread {
//쓰레드 이름 지정 방법
public NameThread() {
//Thread(String name) 호출한 효과
//이 경우, 이 쓰레드 객체 여러 개 생성 시 전부 동일한 이름 가지는 문제 발생
super("ThreadName");
}
//위의 동일한 이름 가지는 여러 개의 쓰레드 생성되는 문제 해결법
public NameThread2(String name) {
super(name);
}
public void run() {
}
}
- run(): 쓰레드 실행 시의 진입점
- start(): 쓰레드 시작
쓰레드 시작 시, 값 동적으로 지정 후 전달하는 방법
package e.thread;
public class NameCalcThread extends Thread {
private int calcNumber;
public NameCalcThread(String name, int calcNumber) {
super(name);
this.calcNumber = calcNumber;
}
public void run() {
calcNumber++;
}
}
- deprecated 메서드 : 더 이상 사용 x
- static 메서드 : 객체 생성하지 않아도 사용 가능
- Thread에 있는 static 메서드 : 대부분 해당 쓰레드 위해 존재하는 것이 아니라, JVM에 있는 쓰레드 관리하기 위한 용도로 사용됨 ➡ 여기서의 예외 중 하나가 sleep() 메서드
sleep() 메서드
| 리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
| static void | sleep(long millis) | 매개 변수로 넘어온 시간(1/1,000초)만큼 대기 |
| static void | sleep(long millis, int nanos) | 첫 번째 매개 변수로 넘어온 시간(1/1,000초) + 두 번째 매개 변수로 넘어온 시간(1/1,000,000,000초) 만큼 대기 |
- run() 메서드 끝나지 않으면 애플리케이션은 끝나지 않음(데몬 쓰레드는 예외)
package e.thread;
public class EndlessThread extends Thread {
public void run() {
while(true) { //break 호출 or 예외 발생시키지 않는 한 멈추지 않음
try {
System.out.println(System.currentTimeMillis());
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
Thread.sleep() 사용 시 유의사항
항상 try-catch로 묶어줘야 함
적어도 InterruptedException으로 catch해줘야 함(반드시 해당 에러가 아니더라도, Exception 같은 상위 예외 사용해도 됨)
Why? sleep() 메서드는 InterruptedException 던질 수도 있다고 선언돼있기 때문
package e.thread;
public class RunEndlessThreads {
public static void main(String[] args) {
RunEndlessThreads sample = new RunEndlessThreads();
sample.endless();
}
public void endless() {
EndlessThread thread = new EndlessThread();
thread.start();
}
}
main() 메서드의 수행이 끝나더라도
main() 메서드나 다른 메서드에서 시작한 쓰레드가 종료되지 않으면
해당 자바 프로세스는 끝나지 않음
(데몬 쓰레드는 예외)
Thread 클래스의 주요 메서드
쓰레드의 속성 확인하고 지정하기 위한 메서드
| 리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
| void | run() | 반드시 구현해야 하는 메서드 |
| long | getId() | 쓰레드의 고유 id 리턴 JVM에서 자동으로 생성해줌 |
| String | getName() | 쓰레드의 이름 리턴 |
| void | setName(String name) | 쓰레드의 이름 지정 |
| int | getPriority() | 쓰레드의 우선 순위 확인 |
| void | setPriority(int newPriority) | 쓰레드의 우선 순위 지정 |
| boolean | isDaemon() | 쓰레드가 데몬인지 확인 |
| void | setDaemon(boolean on) | 쓰레드를 데몬으로 설정할지 아닌지를 설정 |
| StackTraceElement[] | getStackTrace() | 쓰레드의 스택 정보 확인 |
| Thread.State | getState() | 쓰레드의 상태 확인 |
| ThreadGroup | getThreadGroup() | 쓰레드의 그룹 확인 |
쓰레드의 우선 순위(Priority)
쓰레드 우선 순위 = 대기하고 있는 상황엥서 더 먼저 수행할 수 있는 순위
대부분 이 값은 기본값 사용을 권장함(자칫하면 장애로 연결될 수 있기에)
package e.thread;
public class RunDaemonThreads {
public static void main(String[] args) {
RunDaemonThreads sample = new RunDaemonThreads();
sample.checkThreadProperty();
}
public void checkThreadProperty() {
ThreadSample thread1 = new ThreadSample();
ThreadSample thread2 = new ThreadSample();
ThreadSample daemonThread = new ThreadSample();
System.out.println("thread1 id=" + thread1.getId());
System.out.println("thread2 id=" + thread2.getId());
System.out.println("thread1 name=" + thread1.getName());
System.out.println("thread2 name=" + thread2.getName());
System.out.println("thread1 priority=" + thread1.getPriority()); //기본값 = 5
daemonThread.setDaemon(true);
System.out.println("thread1 isDaemon=" + thread1.isDaemon());
System.out.println("daemonThread isDaemon=" + daemonThread.isDaemon());
}
}
//결과
//thread1 id=11
//thread2 id=12
//thread1 name=Thread-0
//thread2 name=Thread-1
//thread1 priority=5
//thread1 isDaemon=false
//daemonThread isDaemon=true
쓰레드 우선순위 관련 상수
| 상수 | 값 및 설명 |
| MAX_PRIORITY | 가장 높은 우선 순위 / 값 = 10 |
| NORM_PRIORITY | 일반 쓰레드의 우선 순위 / 값 = 5 |
| MIN_PRIORITY | 가장 낮은 우선 순위 / 값 = 1 |
우선 순위 정할 때 숫자보다는 위 상수 이용하는 게 better (우선 순위는 되도록 지정하지 않는 게 좋음)
데몬 쓰레드
데몬 쓰레드가 아닌, 사용자 쓰레드는 JVM이 해당 쓰레드가 끝날 때까지 기다림
= 어떤 쓰레드를 데몬으로 지정 시, 그 쓰레드의 수행 여부에 상관 없이 JVM이 끝날 수 있음
단, 해당 쓰레드가 시작하기(start() 메서드가 호출되기) 전에 데몬 쓰레도 지정되어야 함
쓰레드가 시작한 다음에는 데몬으로 지정 불가
package e.thread;
public class DaemonThread extends Thread {
public void run() {
try {
Thread.sleep(Long.MAX_VALUE);
} catch(Exception e) {
e.printStackTrace();
}
}
}
public void runCommonThread() {
DaemonThread thread = new DaemonThread();
thread.setDaemon(true);
thread.start();
}
- 데몬 쓰레드 지정 시, 프로그램이 대기하지 않고 그냥 끝남
- = 데몬 쓰레드는 해당 쓰레드가 종료되지 않아도 다른 실행 중인 일반 쓰레드가 없으면 멈춤
데몬 쓰레도 만든 이유
- 부가적인 작업 수행하는 쓰레드 선언 시 사용하기 위해
- ex) 모니터링하는 쓰레드를 별도로 띄워 모니터링하다, 주요 쓰레드 종료 시 관련된 모니터링 쓰레드가 종료돼야 프로세스가 종료될 수 있음. But 모니터링 쓰레드를 데몬 쓰레드로 만들지 않으면 프로세스 종료 불가하게 됨
synchronized
- 자바의 예약어, 변수명이나 클래스명으로 사용 불가
- 어떤 클래스나 메서드가 쓰레드에 안전하게 하기 위해 사용
- ex) 여러 쓰레드가 한 객체에 선언된 메서드에 접근해 데이터 처리하려 할 때, 동시에 연산을 수행해 값이 꼬이는 경우 발생할 수 있음
- (여기서의 한 객체 = 하나의 클래스에서 생성도니 여러 개의 객체 x, 동일한 하나의 객체)
- 단, 메서드에서 인스턴스 변수 수정하려 할 때만 이런 문제 발생함. 매개 변수나 메서드에서만 사용하는 지역 변수만 다루는 메서드는 synchronized로 선언할 필요 x
synchronized 사용 방법
- 메서드 자체를 synchronized로 선언(synchronized methods)
- 메서드 내의 특정 문장만 synchronized로 감싸기(synchronized statements)
메서드 자체를 synchronized로 선언(synchronized methods)
public synchronized void plus(int value) {
amount += value;
}
- synchronized가 메서드 선언부에 있으면 동일 객체의 이 메서드에 여러 개의 쓰레드가 접근해도 한 순간에는 하나의 쓰레드만 이 메서드를 수행하게 됨
package e.thread.sync;
public class CommonCalculate {
private int amount;
public CommonCalculate() {
amouont = 0;
}
public void plus(int value) {
amount += value;
}
public void minus(int value) {
amount -= value;
}
public int getAmount() {
return amount;
}
}
package e.thread.sync;
public class ModifyAmountThread extends Thread {
private CommonCalculate calc;
private booolean addFlag;
public ModifyAmountThread(CommonCalculate calc, boolean addFlag) {
this.calc = calc;
this.addFlag = addFlag;
}
public void run() {
for(int loop - 0; loop < 10000; loop++) {
if(addFlag) {
calc.plus(1);
} else {
calc.minus(1);
}
}
}
}
package e.thread.sync;
public class RunSync {
public static void main(String[] args) {
RunSync runSync = new RunSync();
runSync.runCommonCalculate();
}
public void runCommonCalculate() {
CommonCalculate calc = new CommonCalculate();
ModifyAmouontThread thread1 = new ModifyAmouontThread(calc, true);
ModifyAmouontThread thread2 = new ModifyAmouontThread(calc, true);
thread1.start();
thread2.start();
try {
thread1.join(); //쓰레드가 종료될 때까지 기다림
thread2.join();
//join() 메서드 수행 이후이므로 모든 쓰레드 종료된 이후의 결과
System.out.println("Final vaue is " + calc.getAmouont());
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
- 위 소스에 대한 결과
Final value is 19511
- RynSync 클래스의 runCommonCalculate() 메서드 수행된 후 두 개의 쓰레드에서 하나의 객체에 있는 amount라는 int 타입의 값에 1을 만번 더한 결과 출력되어야 하는데, 다른 결과가 나옴
- Why? plus() 메서드는 다른 쓰레드에서 작업하고 있더라도 새로운 쓰레드에서 온 작업도 같이 처리하기에 데이터가 꼬이게 됨
| 쓰레드 - 0 | 쓰레드 - 1 | ||
| 현재 연산 | amount의 값 | 현재 연산 | amount의 값 |
| amount + value | 1 | 다른 작업 중 | |
| 더하기 완료 | 1 | amount + value | 1 |
| 좌측 항에 치환 | 2 | 더하기 완료 | 1 |
| 다른 작업 수행 | 좌측 항에 치환 | 2 | |
- 위처럼 동시에 연산 수행되기에 예상 값인 20,000이라는 값 출력되지 못함
- synchronized 추가해서 문제 해결하기
- 해당 메서드는 동일 객체를 참조하는 다른 쓰레드에서 이 메서드를 변경하려 할 때, 먼저 들어온 쓰레드가 종료될 때까지 기다림
- 수정된 메서드
public synchronized void plus(int value) {
amount += value;
}
public synchronized void minus(int value) {
amount -= value;
}
//결과
//Final is 20000
synchronized 블럭
public void plus(int value) {
synchronized(this) {
amount += value;
}
}
public void minus(int value) {
synchronized(this) {
amount -= value;
}
}
synchronized(this) 이후에 있는 중괄호 내의 연산만 동시에 여러 쓰레드에서 처리하지 않겠다는 의미
소괄호 안의 this 부분에는 잠금 처리 하기 위한 객체 선언
Object lock = new Object();
public void plus(int value) {
synchronized(lock) {
amount += value;
}
}
public void minus(int value) {
synchronized(lock) {
amount -= value;
}
}
synchronized 사용 시, 하나의 객체 사용해 블록 내의 문장을 하나의 쓰레드만 수행하도록 할 수 있음
여기서의 lock이나 this: 문지기 역할, 한 명의 쓰레드만 일할 수 있도록 허용 후 다른 쓰레드에 기회 제공
두 개의 별도의 lock 객체 사용해 효율성 높이기
ex) 클래스에 amount라는 변수 외에 interest라는 변수가 있는데, interest 변수 처리 시 여러 쓰레드에서 접근하면 안 되는 경우 발생할 수 있음. 이럴 때 lock이라는 하나의 잠금용 객체만 사용하면 amount 변수 처리 시 interest 변수 부분도 처리 못하게 됨. 이때 별도의 lock 사용해보자.
//before
private int amount;
private int interest;
public void addInterest(int value) {
interest += value;
}
public void plus(int value) {
amount += value;
}
//after
private int amount;
private int interest;
private Object interestLock = new Object();
private Object amountLock = new Object(0;
public void addInterest(int value) {
synchronized(interestLock) {
interest += value;
}
}
public void plus(int value) {
synchronized(amountLock) {
amount += value;
}
}
synchronized 사용 시 유의점
- 메서드를 synchronized할 때는 같은 객체 참조 시에만 유효함
CommonCalculate calc = new CommonCalculate();
ModifyAmountThread thread1 = new ModifyAmountThread(calc, true);
ModifyAmountThread thread2 = new ModifyAmountThread(calc, true);
CommonCalculate calc1 = new CommonCalculate();
ModifyAmountThread thread1 = new ModifyAmountThread(calc1, true);
CommonCalculate calc2 = new CommonCalculate();
ModifyAmountThread thread2 = new ModifyAmountThread(calc2, true);
- 이처럼 두 개의 쓰레드가 동일한 calc가 아닌 서로 다른 객체 참조 시, synchronized로 선언된 메서드는 같은 객체를 참조하는 것이 아니기에 synchronized를 안 쓰는 것과 동일함
- synchronized는 여러 쓰레드에서 하나의 객체에 있는 인스턴스 변수를 동시 처리 시 발생할 수 있는 문제를 해결하기 위해 필요함
- = 인스턴스 변수가 선언되어 있더라도 변수가 선언된 객체를 다른 쓰레드에서 공유할 일이 없다면 synchronized 사용할 이유 없음
| StringBuffer | StringBuilder |
| Thread safe | Thread safe x |
| 하나의 문자열 객체를 여러 쓰레드에서 굥유해야 하는 경우에만 사용 | 여러 쓰레드에서 공유할 일 없을 때 사용 |
쓰레드 통제하는 메서드
| 리턴 타입 | 메서드 이름 및 매개 변수 | 설명 |
| Thread.State | getState() | 쓰레드 상태 확인 |
| void | join() | 수행 중인 쓰레드가 중지할 때까지 대기 |
| void | join(long millis) | 매개 변수에 지정된 시간만큼(1/1,000초) 대기 |
| void | join(long millis, int nanos) | 첫 번째 매개 변수에 지정된 시간(1/1,000초) + 두 번째 매개 변수에 지정된 시간(1/1,000,000,000초)만큼 대기 |
| void | interrupt() | 수행 중인 쓰레드에 중지 요청 |
Thread.State
Thread 클래스의 State라는 enum 클래스에 있는 상수 목록
| 상태 | 의미 |
| NEW | 쓰레드 객체는 생성됐으나 아직 시작되진 않은 상태 |
| RUNNABLE | 쓰레드가 실행 중인 상태 |
| BLOCKED | 쓰레드가 실행 중지 상태이며, 모니터 락(monitor lock)이 풀리길 기다리는 상태 |
| WAITING | 쓰레드가 대기 중인 상태 |
| TIMED_WAITING | 특정 시간만큼 쓰레드가 대기 중인 상태 |
| TERMINATED | 쓰레드가 종료된 상태 |
- 이 클래스는 public static으로 선언됨
- ex) Thread.State.NEW처럼 사용 가능
- 어떤 쓰레드건 NEW ➡ 상태 ➡ TERMINATED 라는 라이프 사이클 가짐
- 여기서 '상태'에 해당하는 것 = NEW, TERMINATED 제외한 모든 것
Thread state diagram

join()
- 매개 변수 없는 join(): 해당 쓰레드가 끝날 때까지 무한대로 대기
thread.join()
- 만약 1분간 대기하고 싶다면
thread.join(60000);
- 더 세부적인 시간대를 지정하고 싶다면
thrad.join(2,3);
//첫 번째 매개 변수 = 2 / 1,000
//밀리초(1 / 1,000초) 단위
//두 번째 매개 변수 = 3 / 1,000,000,000
//나노초(1 / 1,000,000,000초) 단위
//총 대기 시간 = 2 / 1,000 + 3 / 1,000,000,000 = 0.002000003초
interrupt()
- 현재 수행 중인 쓰레드를 InterruptedException 발생시키며 중단시킴
- InterruptedException: sleep()과 join() 메서드에서 발생하는 예외기도 함
- 즉, sleep()과 join() 메서드처럼 대기 상태 만드는 메서드가 호출되면 interrupt() 메서드 호출 가능
- Object 클래스의 wait() 메서드 호출된 상태에서도 이 메서드 사용 가능
stop()
- 안전 상의 이유로 deprecated됨
- interrupt() 사용해 쓰레드 멈춰야 함
package e.thread.support;
public class SleepThread extends Thread {
long sleepTime;
//생성자에서 밀리초 단위 시간을 매개 변수로 받아 인스턴스 변수로 지정
public SleepThread(long sleepTime) {
this.sleepTime = sleepTime;
}
//여기서 위의 인스턴스 변수에 지정된 시간만큼 잠을 잠
public void run() {
try {
System.out.println("Sleeping " + getName());
Thread.sleep(sleepTime);
System.out.println("Stopping " + getName());
} catch(InterruptedException ie) {
ie.printStackTrace();
}
}
}
package e.thread.support;
public class RunSupportThreads {
public static void main(String[] args) {
RunSupportThreads sample = new RunSupportThreads();
sample.checkThreadState1();
}
public void checkThreadState1() {
SleepThread thread = new SleepThread(2000);
try {
System.out.println("thread state = " + thread.getState());
thread.start();
System.out.println("thread state(after start) = " + thread.getState());
thread.sleep(1000);
System.out.println("thread state(after 1 sec) = " + thread.getState());
thread.join();
thread.interrupt();
System.out.println("thread state(after join) = " + thread.getState());
} catch(InterruptedException ie) {
ie.printStackTrace();
}
}
}
/*
결과
thread state = NEW -- 1
thread state(after start) = RUNNABLE -- 2
Sleeping Thread-0
thread state(after 1 sec) = TIMED_WAITING -- 3
Stopping Thread-0
thread state(after join) = TERMINATED -- 4
*/
- 아직 쓰레드 시작 x, Thread.State 중 NEW 상태
- 쓰레드 시작한 상황, 아직 첫 출력문까지 도달하지 않았기에 RUNNABLE 상태
- 2초간 잠자는 모드이기에 TIMED_WAITING
- 쓰레드가 종료되길 join() 메서드에서 기다린 후의 상태이기에 TERMINATED 상태
P.690 checkJoin() ~
'Backend > Java' 카테고리의 다른 글
| [자바의 신] 27장 (ongoing) (0) | 2022.08.18 |
|---|---|
| [자바의 신] 26장 (ongoing) (0) | 2022.08.18 |
| [자바의 신] 24장 (0) | 2022.08.09 |
| [자바의 신] 23장 (0) | 2022.08.09 |
| [자바의 신] 22장 (0) | 2022.08.09 |
댓글