프로세스와 쓰레드 (process & thread)
프로세스 : 실행중인 프로그램, 자원(resources)과 쓰레드로 구성
자원이란 메모리, Cpu와 같은 것을 말함
쓰레드 : 프로세스 내에서 실제 작업을 수행, 모든 프로세스는 최소한 하나의 쓰레드를 가지고 있다.
하나의 새로운 프로세스를 생성하는 것보다 하나의 새로운 쓰레드를 생성하는 것이 더 적은 비용이 든다.
멀티쓰레드의 장단점
대부분의 프로그램이 멀티쓰레드로 작성되어 있지만, 멀티쓰레드 프로그래밍이 장점만 있는 것은 아니다.
장점 | - 시스템 자원을 보다 효율적으로 사용할 수 있다. - 사용자에 대한 응답성(responseness)이 향상된다. - 작업이 분리되어 코드가 간결해 진다. |
단점 | - 동기화(synchronization)에 주의해야 한다. - 교착상태(dead-lock)이 발생하지 않도록 주의해야 한다. - 각 쓰레드가 효율적으로 고르게 실행될 수 있게 해야 한다. - 프로그래밍할 때 고려해야 할 사항들이 많아진다. |
쓰레드의 구현과 실행
쓰레드의 구현
쓰레드를 구현하는 방법은 Thread클래스를 상속하는 방법과 Runnable 인터페이스를 구현하는 방법 두가지가 있다.
Thread클래스를 상속
- 자바는 단일 상속만 허용하기 때문에 Thread를 상속 받는다면 다른 클래스를 상속 받을 수 없게 된다.
class MyThread extends Thread{
public void run() { // Thread 클래스의 run()을 오버라이딩
}
}
- Thread클래스를 상속해서 구현 한 쓰레드 실행
MyThread t1 = new MyThread(); // 쓰레드 생성
t1.start(); // 쓰레드 실행
Runnable인터페이스를 구현
- Runnable인터페이스를 구현 해도 다른 클래스를 상속받을 수 있기 때문에 유연하다.
class MyThread2 implements Runnable{
public void run() { // Runnable 인터페이스의 run()을 구현
}
}
// Runnable 인터페이스
//public interface Runnable {
// public abstract void run();
//}
- Runnalbe 인터페이스를 구현해 구현한 쓰레드 실행
// Runnable 인터페이스 타입의 참조변수로 Runnable를 구현한 MyThread2를 생성한 뒤 참조
Runnable r = new MyThread2();
// Thread (Runnalbe r), Thread 클래스의 생성자를 사용
Thread t2 = new Thread(r);
//Thread t2 = new Thread(new MyThread2()); 한줄로 작성
t2.start();
- run() 메서드의 구현체를 외부에서 매개변수로 받는 것
쓰레드의 실행
쓰레드를 생성한 후에 start()를 호출해야 쓰레드가 작업을 시작한다.
멀티 쓰레드 실행 예시
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread(); // 쓰레드 생성
Thread t2 = new Thread(new MyThread2()); // 생성자 Thread(Runnable r)
t1.start(); // 쓰레드 실행
t2.start();
}
}
class MyThread extends Thread{ // 1. Thread 클래스를 상속해서 쓰레드 구현
public void run() { // 쓰레드가 수행할 작업
for(int i = 0; i < 50; i++)
System.out.print(0);
}
}
class MyThread2 implements Runnable{ // 2. Runnalbe 인터페이스를 구현해서 쓰레드를 구현
public void run() { // 쓰레드가 수행할 작업
for(int i = 0; i < 50; i++)
System.out.print(1);
}
}
싱글 쓰레드 실행 결과
public class Main {
public static void main(String[] args) {
for(int i = 0; i < 50; i++)
System.out.print(0);
for(int i = 0; i < 50; i++)
System.out.print(1);
}
}
- 싱글 스레드로 실행 한다면 작업이 다 끝나야 다음 작업이 실행 되는 것을 볼 수 있다.
0000000000000000000000000000000000000000000000000011111111111111111111111111111111111111111111111111
- A작업을 다 마치고 B작업을 수행하기 때문에 일정하다.
멀티 쓰레드 실행 결과
- 두가지 작업번걸아 가면서 수행한 것을 볼 수 있다.
0000000000000000000000000011110011111100001100000111111111111111001111111111111111110001111100000000
- 싱글 쓰레드를 사용할 때보다 더 많은 시간을 소요하게 된다.
쓰래드 간의 작업 전환(문맥 전환, context switching )에 시간이 걸리기 때문이다. 예를 들어 작업 전환 시 현재 진행중인 작업의 상태, 다음에 실행해야할 위치 등의 정보를 저장하고 읽어 오는 시간이 소요된다.
프로세스의 스위칭에서는 더 많은 시간이 소요된다. 그래서 싱글코어라면 단순한 계산작업의 경우 멀티 쓰레드보다 싱글 쓰레드로 프로그래밍 하는 것이 더 효율적이다.
- 실행할 때 마다 결과가 달라진다.
1100111111111110000000111111111111111110000111111111111111111110000000000000000000000000000000000000
start()를 호출하더라도 즉시 작업이 실행되는 것이 아니며, 기존 호출된 start()가 존재하더라도 우선적으로 실행되는 것이 아니라 OS 프로세스 스케쥴러에 의해 스레드의 실행 순서 시간이 정해지기 때문이다.상황에 따라 프로세스에게 할당되는 시간이 일정하지 않고 쓰레드에게 할당되는 시간 역시 일정하지 않게 된다. 따라서 실행할 때 마다 실행 결과가 달라지며 스레드가 실행될 순서와 시간을 지정할 수 없다.
왜 start()로 실행을 할까?
- 쓰레드의 실행 내용을 run() 메서드에 작성하는데 왜 start()로 실행을 할까?
각각의 스레드가 별도의 호출 스택을 가지고 독립적인 작업을 수행하기 위해서 start()를 사용한다.
만약 run()을 호출하여 작업을 수행한다면 새로운 호출 스택 생성이 없어 단일 쓰레드와 같이 동작 한다.
- main() 메서드에서 start()메서드를 호출
- start()메서드는 새로운 쓰레드를 생성하고, 쓰레드가 작업하는데 사용될 새로운 호출 스택을 생성
- 새로 생성된 호출 스택에 run()이 호출되어, 쓰레드가 독립된 공간에서 작업을 수행한다.
- 할일을 마친 start() 메서드는 종료, 2개의 호출 스택이 스케줄러가 정한 순서에 의해서 번갈아 가며 실행된다.
프로그램은 실행 중인 사용자 쓰레드가 하나도 없을 때 종료된다.
- 쓰레드는 '사용자 쓰레드'와 '데몬 쓰레드' 두 종류가 있다. '사용자 쓰레드'가 하는 작업을 보조하는 역할을 하는 쓰레드를 '데몬 쓰레드'라 한다.
main쓰레드
- main메서드의 코드를 수행하는 쓰레드를 말한다.
public class Main {
public static void main(String[] args) {
System.out.println("main 쓰레드");
}
}
프로그램 실행을 위해서는 최소한 하나의 쓰레드가 필요하기 때문에 프로그램이 실행하면 기본적으로 하나의 쓰레드를 생성하고 새로운 호출 스택이 생성되어 그 쓰레드가 main 메서드를 호출해서 작업을 수행 하도록 한다.
- main쓰레드는 그 중 '사용자 쓰레드'에 속하기 때문에 main 쓰레드가 실행을 종료해도 아직 실행중인 쓰레드가 존재한다면 프로그램은 종료되지 않는다.
public class Main {
static long startTime = 0;
public static void main(String args[]) {
ThreadEx11_1 th1 = new ThreadEx11_1();
ThreadEx11_2 th2 = new ThreadEx11_2();
th1.start();
th2.start();
startTime = System.currentTimeMillis();
// try {
// th1.join(); // main쓰레드가 th1의 작업이 끝날 때까지 기다린다.
// th2.join(); // main쓰레드가 th2의 작업이 끝날 때까지 기다린다.
// } catch(InterruptedException e) {}
System.out.print("소요시간:" + (System.currentTimeMillis() - Main.startTime));
} // main
}
class ThreadEx11_1 extends Thread {
public void run() {
for(int i=0; i < 300; i++) {
System.out.print(new String("-"));
}
} // run()
}
class ThreadEx11_2 extends Thread {
public void run() {
for(int i=0; i < 300; i++) {
System.out.print(new String("|"));
}
} // run()
}
- main 쓰레드가 종료되어도 다른 쓰레드가 종료되지 않아 프로그램이 종료되지 않는 것을 볼 수 있다.
----------------소요시간:0(main 쓰레드 종료)
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---------||||||||||----------||||||||||||||||||||||||||||||||||||||||||||||--|||
|||-|||||||||||||||||-----------------------------------------------------------
---||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||-----------------
--------|||||||||||||||||||||||||||||||||||||-------------------------||||||||||
||||||||||||||||||||||||||||||||||(th2 쓰레드 종료)-----------------------------
--------------------------------------------------------------------------------
-----------------------------------------(th1 쓰레드 종료)
쓰레드의 I/O 블락킹(Input/Output blocking)
I/O 블락킹은 입출력(입력/출력)시 작업을 중단하는 것을 말한다.
- 싱글 스레드로 작업 시 사용자가 입력을 하지 않는 다면 작업을 중단하고 기다리며 아무 작업도 하지 않는다.
public class Main {
public static void main(String args[]) {
// A : 입력 받기
String input = JOptionPane.showInputDialog("입력");
System.out.println("입력값은 "+ input +"입니다.");
// B : 카운트 다운
for(int i = 10; i > 0; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
} // main
}
- 입력을 하지 않는 시간 동안 프로그램은 정지하게 되며 사용자가 입력을 마쳐야 다음 작업으로 넘어간다.
- B 작업을 별도의 쓰레드로 분리하여 멀티 쓰레드 프로그램으로 작성을 한다면 입력을 하지 않는 시간 동안 프로그램이 정지되지 않고 B 작업을 수행하게 된다.
public class Main {
public static void main(String args[]) {
Thread1 th1 = new Thread1();
th1.start();
// A : 입력 받기
String input = JOptionPane.showInputDialog("입력");
System.out.println("입력값은 "+ input +"입니다.");
} // main
}
class Thread1 extends Thread {
public void run() {
// B : 카운트 다운
for(int i = 10; i > 0; i--) {
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
} // run()
}
109876입력값은 입력입니다.54321
- 이처럼 멀티쓰레드를 활용 한다면 한 작업을 수행되는 동안 다른 외부적인 요인에 의해 멈춰 있더라도 다른 쓰레드가 작업을 수행할 수 있어 보다 작업을 효율적으로 빨리 끝낼 수 있다는 장점이 있다.
쓰레드의 우선순위(priority of thread)
자바에서 각 쓰레드는 우선순위(priority)에 관한 자신만의 필드를 가지고 있다. 작업의 중요도에 따라 쓰레드의 우선순위를 다르게 하여 특정 쓰레드가 더 많은 작업 시간을 갖게 설정할 수 있다.
static int MAX_PRIORITY | 쓰레드가 가질 수 있는 최대 우선순위를 명시함 |
static int MIN_PRIORITY | 쓰레드가 가질 수 있는 최소 우선순위를 명시함 |
static int NORM_PRIORITY | 쓰레드가 생성될 때 가지는 기본 우선순위를 명시함 |
- 자바에서는 쓰레드의 우선순위를 1부터 10까지 보유할 수 있으며 숫자가 높을수록 우선순위가 높아진다.
하지만 우선순위는 비례적인 절대값이 아닌 상대적인 값으로 우선순위가 10인 쓰레드가 우선순위가 1인 쓰레드보다 10배 더 빨리 수행되는 것이 아니라 우선순위가 10인 쓰레드는 좀 더 많이 실행 큐에 포함되어, 좀 더 많은 작업 시간을 할당받는것이다. (1~10 은 JVM에서 정해 놓은 우선순위 범위이다)
- setPriority() 메서드와 getPriority()메서드를 통해 스레드의 우선순위를 변경하거나 반환할 수있다.
- 우선순위는 쓰레드가 시작된 이후에도 변경이 가능하다.
void setPriority(int newPriority) | 쓰레드의 우선순위를 지정한 값으로 변경한다. |
int getPriority() | 쓰레드의 우선순위를 반환한다. |
- 우선순위는 해당 쓰레드를 생성한 쓰레드의 우선순위를 상속받는다.
class Thread1 extends Thread {
public void run() {
for(int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
try {Thread.sleep(1000);}
catch(Exception e) {}
}
} // run()
}
main()메서드를 실행하는 쓰레드의 우선순위는 5이므로 main 메서드 내에 생성된 쓰레드 Thead-1의 우선순위는 5이다.
public class Main {
public static void main(String args[]) {
Thread1 th1 = new Thread1();
Thread1 th2 = new Thread1();
th1.setPriority(10); // Thread-0의 우선순위 변경
th1.start();
th2.start();
System.out.println("Thread-0의 우선 순위 : " + th1.getPriority());
System.out.println("Thread-1의 우선 순위 : " + th2.getPriority());
} // main
}
우선순위가 높은 Thread-0이 반드시 먼져 실행되고 종료되는 것이 아니다 좀더 많은 작업 시간을 할당 받았을 뿐이다.
Thread-0의 우선 순위 : 10
Thread-1의 우선 순위 : 5
Thread-1
Thread-0
Thread-0
Thread-1
Thread-0
.....
.....
쓰레드 그룹(thread group)
쓰레드 그룹이란 서로 관련이 있는 쓰레드를 하나의 그룹으로 묶어서 다루기 위한 장치를 말한다.이를 위해 자바에서는 쓰레드 그룹을 다루기 위해 ThreadGroup 클래스를 제공한다.
쓰레드 그룹은 다른 쓰레드 그룹을 포함할 수도 있다지만 쓰레드는 자신이 포함된 쓰레드 그룹과 그 하위 그룹에 접근할 수 있지만, 다른 그룹에는 접근할 수 없다.
- 모든 쓰레드는 반드시 하나의 쓰레드 그룹에 포함되어 있어야 한다.
Thread(ThreadGroup group, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
- 쓰레드는 자신을 생성한 쓰레드(부모 쓰레드)의 그룹과 우선순위를 상속 받기 때문에 쓰레드 그룹을 지정하지 않고 생성한 쓰레드는 'main쓰레드 그룹'에 속한다.
Thread()
Thread(Runnable target)
- getThreadGroup() 메서드를 통해 쓰레드 자신이 속한 그룹을 반환할 수있으며 uncaughtException ()메서드를 오버라이딩 해 예외 발생시 처리를 변경할 수 있다.
ThreadGroup getThreadGroup() | 쓰레드 자신이 속한 쓰레드 그룹을 반환한다. |
void uncaughtException(Thread t, Throwable e) | 처리되지 않은 예외에 의해 쓰레드 그룹의 쓰레드가 실행이 종료되었을 때, JVM에 의해 이 메서드가 자동적으로 호출된다. (ThreadGroup 클래스에 속한 메서드) |
class Thread1 implements Runnable {
public void run() {
} // run()
}
public class Main {
public static void main(String args[]) {
Thread thread0 = new Thread(new Thread1()); // main 쓰레드에 속하게 됨
thread0.start();
System.out.println("thread0의 그룹 : " +thread0.getThreadGroup());
ThreadGroup group = new ThreadGroup("my_Group"); // my_Group라는 쓰레드 그룹 생성
group.setMaxPriority(7); // my_Group 그룹의 최대 우선순위를 변경
// 쓰레드 생성 시 포함될 쓰레드 그룹my_Group을 전달
Thread thread1 = new Thread(group, new Thread1());
thread1.start();
System.out.println("thread1의 그룹 : " +thread1.getThreadGroup());
} // main
}
thread0의 그룹 : java.lang.ThreadGroup[name=main,maxpri=10]
thread1의 그룹 : java.lang.ThreadGroup[name=my_Group,maxpri=7]
데몬 쓰레드(deamon thread)
데몬 쓰레드는 일반 쓰레드(non-deamon thread)의 작업을 돕는 보조적인 역할을 수행하는 쓰레드를 말한다.
- 일반 쓰레드가 모두 종료되면 자동적으로 종료된다.
- 가비지 컬렉터, 자동저장, 화면 자동갱신 등에 사용된다.
무한루프와 조건문을 이용해서 실행 후 대기하다가 특정 조건이 만족되면 작업을 수행하고 다시 대기하도록 작성한다.
- 일반 쓰레드가 모두 종료되면 자동적으로 종료되기 때문에 무한루프로 작성해도 문제가 없다.
class Main implements Runnable {
static boolean autoSave = false;
// main 쓰레드
public static void main(String[] args) {
Thread t = new Thread(new Main());
t.setDaemon(true); // 이 부분이 없으면 종료되지 않는다.
t.start();
for(int i=1; i <= 10; i++) {
try{
Thread.sleep(1000);
} catch(InterruptedException e) {}
System.out.println(i);
if(i==5) autoSave = true;
}
System.out.println("프로그램을 종료합니다.");
}
// 데몬 쓰레드
public void run() {
while(true) {
try {
Thread.sleep(3 * 1000); // 3초마다
} catch(InterruptedException e) {}
// autoSave의 값이 true이면 autoSave()를 호출한다.
if(autoSave) autoSave();
}
}
public void autoSave() {
System.out.println("작업파일이 자동저장되었습니다.");
}
}
boolean is Deamon() | 쓰레드가 데몬 쓰레드인지 확인한다. 데몬 쓰레드이면 true를 반환한다. |
void setDeamon(boolean on) | 쓰레드를 데몬 쓰레드로 또는 사용자 쓰레드로 변경한다, 매개변수on을 true로 지정하면 데몬 쓰레드가 된다. |
setDeamon(boolean on)은 반드시 쓰레드를 생성한 뒤 start()를 호출하기 전에 실행되어야 한다. 실행한 다음에는 쓰레드를 데몬 쓰레드/ 일반 쓰레드로 변경할 수 없기 때문에 IllegalThreadStateException이 발생한다.
쓰레드의 상태
상태 | 설명 |
NEW | 쓰레드가 생성되고 아직 start()가 호출되지 않은 상태 |
RUNABLE | 실행 중 또는 실행 가능한 상태 |
BLOCKED | 동기화블럭에 의해서 일시정지된 상태(lock이 풀릴 때까지 기다리는 상태) |
WAITING, TIMED_WAITING | 쓰레드의 작업이 종료되지는 않았지만 실행가능하지 않은(unrunnalbe) 일시정지상태 TIMED_WAITING은 일시정지시간이 지정된 경우를 의미 |
TERMINATED | 쓰레드의 작업이 종료된 상태 |
start()를 호출하면 RUNNABLE 상태가 된다. (실행대기/실행)
start()가 호출되더라도 바로 실행되는 것이 아니다 앞서 실행상태가 된 쓰레드가 종료 되어 차례가 된다면 특정 시간 동안 실행 되고 작업을 다 마치지 못했다면 다시 실행 대기 상태가 되어 대기하게 되는 과정을 반복한다.
stop()이 호출되거나 주어진 작업을 다 마친다면 TERMINATED 상태가 된다.
쓰레드가 실행되다 suspend(), sleep(), wait(), join()이 호출 되거나 I/O block이 발생하면 WAITING/BLOCKED상태가 된다. (일시정지 상태가 된다.)
- suspend() : 일시정지 , sleep() : 잠자기 , wait() : 대기 , join() : 다른 쓰레드 대기
쓰레드의 실행제어
쓰레드의 실행을 제어할 수 있는 메서드가 제공된다.
이 들을 활용해서 보다 효율적인 프로그램의 작성할 수 있다.
쓰레드 자기 자신에서만 호출 가능 sleep, yield
메서드 | 설명 |
static void sleep(long millis) static void sleep(long millis, int nanos) |
지정된 시간(천분의 일초 단위)동안 쓰레드를 일시정지시킨다. 지정한 시간이 지나고 나면, 다시 실행대기상태가 된다. ( 잠자기 ) |
void join() void join(long millis) void join(long millis, int nanos) |
지정된 시간동안 쓰레드가 실행되도록 한다. 지정된 시간이 지나거나 작업이 종료되면 join()을 호출한 쓰레드로 다시 돌아와 실행을 계속한다. ( 다른 쓰레드 대기 ) |
void interrupt() | sleep()이나 join()에 의해 일시정지상태인 쓰레드를 깨워서 실행대기상태로 만든다. 해당 쓰레드에서는 Interrupted Exception이 발생함으로써 일시정지 상태를 벗어나게 된다. |
void stop() | 쓰레드를 즉시 종료시킨다. |
void suspend() | 쓰레드를 일시정지시킨다. resume()을 호출하면 다시 실행대기상태가 된다. (일시정지) |
void resume() | suspend()에 의해 일시정지상태에 있는 쓰레드를 실행대기상태로 만든다. |
static void yield() | 실행 중에 자신에게 주어진 실행시간을 다른 쓰레드에게 양보(yield)하고 자신은 실행대기상태가 된다. |
sleep(), yield()는 static 메서드로 자기 자신에 한정된다.
sleep()
현재 쓰레드를 지정된 시간동안 멈추게 한다
- 예외 처리를 필수로 해야 한다.( InterruptedException이 발생하면 깨어남)
- InterruptedException은 인터럽트가 발생해 대기 중이던 상태가 예상보다 빨리 끝났다는 것을 뜻한다.
InterruptedException이 발생하는 경우
1. sleep()메서드가 호출상태(대기상태인 도중)에서 쓰레드의 interrupted 상태가 true로 되는 경우
2. 인터렙트 상태가 true인 상태에서 sleep() 메서드가 호출되는 경우
InterruptedException이 발생한 후 스레드의 인터럽트 상태는 자동으로 false로 변경된다
-> catch문에서 인터럽트 상태를 확인해보면 false를 확인 할 수 있다.
- 문제가 있어 예외가 발생한 것이 아니라 예외와 try-catch문을 활용해 잠자는 상태를 벗어나기 위한것이다.
public static native void sleep(long millis) throws InterruptedException;
try {
Thread.sleep(0);
} catch (InterruptedException e) {}
.특정 쓰레드를 지정해서 멈추게 하는 것은 불가능하다.
try {
t1.sleep(2000); // 바람직 하지 않다. Thread.sleep(2000);
} catch (InterruptedException e1) {}
Interrupt()
대기상태(WAITING)인 쓰레드를 실행대기 상태(RUNABLE)로 만든다.
void Interrupt() | 쓰레드의 interrupted상태를 false에서 true로 변경한다. |
boolean isInterrupted() | 쓰레드의 interrupted상태를 반환한다. |
static boolean interrupted() | 현재 쓰레드의 interrupted 상태를 알려주고, interrupted를 false로 초기화한다. |
어떤 작업을 수행하는 다른 쓰레드를 멈추게 할 수 있다.
import javax.swing.JOptionPane;
class Main {
public static void main(String[] args){
Thread1 th1 = new Thread1();
th1.start();
System.out.println("isInterrupted() : " + th1.isInterrupted());
String input = JOptionPane.showInputDialog("종료하시겠습니까?.");
th1.interrupt(); // interrupt()를 호출하면, interrupted상태가 true가 된다.
try {
th1.join();
} catch (InterruptedException e) {}
System.out.println("isInterrupted():"+ th1.isInterrupted()); // true
}
}
class Thread1 extends Thread {
public void run() {
int i = 10;
while(i!=0 && !isInterrupted()) {
System.out.println(i--);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {interrupt();}
// catch문에 interrupt()를 작성하지 않는다면 카운트가 멈추지 않는다.
}
System.out.println("카운트가 종료되었습니다.");
}
}
isInterrupted() : false
10
9
카운트가 종료되었습니다.
isInterrupted():true
catch문에 interrupt()를 작성하지 않는다면 카운트가 멈추지 않는다. 이는 sleep()이 호출된 상태인 쓰레드에 intterupt()가 호출되어 interrupted 상태가 true가 되었기 때문이다. 이때 InterruptedException이 발생되는데 InterruptedException이 발생 된다면 쓰레드의 interrupted 상태는 자동으로 false로 초기화 되기 때문이다.
suspend(), resume(), stop()
쓰레드의 실행일 일시정지, 재개, 완전정지 시킨다.
교착상태( dead-lock )에 빠지기 쉬워서 deprecated되었다. (사용을 권장하지 않는다)
void suspend() | 쓰레드를 일시정지 시킨다. |
void resume() | suspend()에 의해 일시정지된 쓰레드를 실행대기상태로 만든다. |
void stop() | 쓰레드를 즉시 종료시킨다.(TERMINATED) |
join()
지정된 시간동안 특정 쓰레드가 작업하는 것을 기다린다.
void join() | 작업이 모두 끝날 때까지 기다린다. |
void join (long millis) | 천분의 일초 동안 기다린다. |
void join(long millis, int nanos) | 천분의 일초 + 나노초 동안 기다린다. |
예외처리를 해야 한다. (InterruptedException이 발생하면 작업 재개)
yield()
남은 시간을 다음 쓰레드에게 양보하고, 자신(현재 쓰레드)은 실행대기 상태가 된다.
- yield()와 interrupt()를 적절히 사용하면, 응답성과 효율을 높일 수 있다.
일시정지 상태가 되어 작업을 수행을 하지 못하게 되어도 하는 일 없이 반복문을 계속 수행하게 된다.
(busy-waiting, 자원을 얻기 위해 기다리는 것이 아닌 권한을 얻을 때까지 확인하는 것을 말한다.)
public void run() {
while(!stopped) {
if(!suspended) {
try {
Thread.sleep(1000);
}catch(InterruptedException e) {}
}else {
Thread.yield();
}
}
}
이 떄 yield를 사용해 권한을 확보하지 못한다면 남은 시간을 다음 쓰레드에게 양보하는 것이 효율적이다.
'Java' 카테고리의 다른 글
[JAVA] 자바의 입출력과 스트림(I/O stream) (0) | 2024.01.18 |
---|---|
[JAVA] 쓰레드의 동기화(synchronization) (0) | 2024.01.18 |
[JAVA] 애너테이션(Annotation) (0) | 2024.01.10 |
[JAVA] 열거형(enum) (0) | 2024.01.05 |
[JAVA] 제네릭(Generics) (0) | 2023.11.25 |