21일차 배운 내용
(오늘 배운 내용은 다른 내용들에 비해 엄청 중요한 내용은 아니라고 강사님이 말씀하심! 히희,, 가벼운 마음으로 들었다,, )
1. 스레드 상태
- 스레드의 일반적인 상태
- 스레드에 일시 정지 상태를 도입한 경우
2. 스레드 상태 제어
- sleep() 메서드 : 주어진 시간동안 스레드 일시 정지
- yield() 메서드 : 다른 스레드에게 실행을 양보하고 본인은 실행 대기 상태가 됨
- join() 메서드 : 다른 스레드의 종료를 기다림
- stop() 메서드와 stop 플래그 : 실행 중인 스레드 즉시 종료
- interrupt() 메서드 : 스레드 일시 정지 상태일 때 예외 발생시켜 종료시킴
3. 데몬 스레드
4. 스레드풀
[ 스레드 상태 ]
1. 스레드의 일반적인 상태
2. 스레드에 일시 정지 상태 도입한 경우
public class ThreadStateExample {
public static void main(String[] args) {
// 작업 스레드 생성
StatePrintThread statePrintThread = new StatePrintThread(new TargetThread());
statePrintThread.start();
}
}
// 하위 클래스
public class TargetThread extends Thread {
public void run() {
for(long i=0; i<1000000000; i++) {}
try {
//1.5초간 일시 정지
Thread.sleep(1500);
} catch(Exception e) {}
for(long i=0; i<1000000000; i++) {}
}
}
public class StatePrintThread extends Thread {
private Thread targetThread;
public StatePrintThread(Thread targetThread) {
this.targetThread = targetThread;
}
public void run() {
while(true) {
Thread.State state = targetThread.getState();
System.out.println("타겟 스레드 상태: " + state);
if(state == Thread.State.NEW) {
targetThread.start();
}
if(state == Thread.State.TERMINATED) {
break;
}
try {
//0.5초간 일시 정지
Thread.sleep(500);
} catch(Exception e) {}
}
}
}
[ 스레드 상태 제어 ]
- 스레드 상태 제어란 실행 중인 스레드의 상태를 변경하는 것이다.
- 상태 변화를 가져오는 메서드의 종류가 여러가지가 있는데 그 중 다음에 나오는 메서드들만 알아보자.
( 일단 아래의 사진과 같은 메서드들이 있는데 설명되어있는 메서드 외 나머지 메서드들은 잘 몰라도 됨,, )
1) sleep( ) : 주어진 시간동안 스레드를 일시 정지해주는 메서드
- 얼마 동안 일시 정지 상태로 있을 것인지 밀리 세컨드 단위(1/1000) 로 지정해준다.
- 일시 정지 상태에서 interrupt() 메소드 호출하면 InterruptedException 발생한다.
import java.awt.Toolkit;
public class SleepExample {
public static void main(String[] args) {
Toolkit toolkit = Toolkit.getDefaultToolkit();
for(int i=0; i<10; i++) {
toolkit.beep();
try {
Thread.sleep(3000);
} catch(InterruptedException e) {
}
}
}
}
// 주어진 시간동안 일시 정지(sleep())
// 얼마 동안 일시 정지 상태로 있을 것인지 밀리 세컨드(1/1000) 단위로 지정
2) yield() : 다른 스레드에게 실행 양보해주는 메서드
- 프로그램 실행 중에 우선순위가 동일하거나 높은 우선순위를 가진 다른 스레드에게 실행을 양보하고
본인은 실행 대기 상태가 된다.
public class ThreadA extends Thread {
public boolean stop = false;
public boolean work = true;
public void run() {
while(!stop) {
if(work) {
System.out.println("ThreadA 작업 내용");
} else {
Thread.yield(); // ThreadB 작업 내용 위주로 실행된다.
}
}
System.out.println("ThreadA 종료");
}
}
public class ThreadB extends Thread {
public boolean stop = false;
public boolean work = true;
public void run() {
while(!stop) {
if(work) {
System.out.println("ThreadB 작업 내용");
} else {
Thread.yield(); // ThreadA 작업 내용 위주로 실행된다.
} //다른 스레드에게 실행 양보(yield())
// 실행 중 우선순위가 동일한 다른 스레드에게 실행 양보후 실행 대기 상태가 됨
}
System.out.println("ThreadB 종료");
}
}
public class YieldExample {
public static void main(String[] args) {
ThreadA threadA = new ThreadA();
ThreadB threadB = new ThreadB();
threadA.start();
threadB.start();
try {
Thread.sleep(3000); // 3초동안을 의미
} catch (InterruptedException e) {
}
threadA.work = false; // threadA가 양보 -> threadB만(위주로) 실행됨
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
threadA.work = true; // threadA가 양보안함 -> round-robin 방식으로 번갈아가며 실행됨
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
threadA.stop = true;
threadB.stop = true;
}
}
3) join() : 다른 스레드가 종료될 때까지 기다리게하는 메서드
- 계산 작업을 하는 스레드가 모든 계산 작업을 마쳤을 때, 결과값을 받아 이용하는 경우에 주로 사용한다.
public class SumThread extends Thread {
private long sum;
public long getSum() {
return sum;
}
public void setSum(long sum) {
this.sum = sum;
}
public void run() {
for(int i=1; i<=100; i++) {
sum+=i;
}
}
}
public class JoinExample {
public static void main(String[] args) {
SumThread sumThread = new SumThread();
sumThread.start();
try {
sumThread.join();
} catch (InterruptedException e) {
}
System.out.println("1~100 합: " + sumThread.getSum());
}
}
4) stop() 메서드와 stop 플래그
- stop( ) 메서드 사용시, 스레드가 즉시 종료되어 편리하다.
하지만 즉시 종료가 되는거라 사용 중이던 자원들이 불완전한 상태로 남겨지며 종료가 된다.
이렇게 사용 중이던 자원들이 불완전한 상태로 남겨지는 것을 Deprecated 라고 한다.
--> stop() 메서드는 사용하는 것 완전 비추,,,
그렇담 안전하게 종료할 수는 없는 걸까..?!
--> 그럴 땐 stop 플래그를 이용한다!!
- stop 플래그
: stop 플래그를 사용하면 메서드를 정상적으로 종료할 수 있도록 유도하기 때문에 안전하게 종료할 수 있다.
public class PrintThread1 extends Thread {
private boolean stop; // 디폴트값 : false -> true
public void setStop(boolean stop) {
this.stop = stop;
}
public void run() {
while(!stop) { // (stop flag) stop 플래그 방법
System.out.println("실행 중");
}
System.out.println("자원 정리");
System.out.println("실행 종료");
}
}
5) interrupt() 메서드
- 스레드가 일시 정지 상태일 경우에 interrupt() 메서드를 사용하면 InterruptedException 예외를 발생시킨다,
- 일시 정지 상태가 아닌 실행대기 또는 실행상태에서 interrupt() 메서드를 사용하면 InterruptedException 예외가
발생되지 않는다. (= 일시 정지 상태인 경우에만 interrupt() 메서드가 효과가 있다. 그 외는 효과 없음)
- 이렇게 일시 정지 상태가 아닐때는 일시정지 상태로 만들어서 interrupt() 메서드를 사용해야할까? NO!!
- 일시정지가 아닐때는 interrupted() 메서드와 isInterrupted() 메서드를 사용해서 interrupt() 메서드를 호출하면
true값이 반환되어 반복문(?)을 빠져나갈 수 있다,,
* 일시 정지 상태일때 interrupt() 메서드를 실행해야 예외가 발생되어 try-catch()문을 빠져나갈 수 있게된다.
그런데 일시 정지 상태가 아닌 실행대기나 실행상태에서 interrupt( ) 메서드를 실행해도 Exception이 발생되지 않아
빠져나가지 못하고 계속 무한반복이 실행된다. *
public class PrintThread2 extends Thread {
public void run() {
//how1 : thread.interrupt()를 실행시켜
// 스레드 상태를 일시정지 상태로 변경하여 예외(Exception)를 발생시켜 실행 종료시킴
/*try {
while(true) {
System.out.println("실행 중");
Thread.sleep(1); // 얘가 없으면 무한 실행한다.
}
} catch(InterruptedException e) {
}*/
//how2 : runnable(실행대기)과 run(실행) 상태인 경우,
// Thread.interrupt()를 실행시켜도 예외가 발생하지 않기때문에
// Thread.interrupted()를 이용해서 -> while문 탈출 -> 실행 종료시킴
while(true) {
System.out.println("실행 중");
/*
if(Thread.interrupted()) {
break;
}
*/
}
//System.out.println("자원 정리");
//System.out.println("실행 종료");
}
}
public class StopFlagExample {
public static void main(String[] args) {
PrintThread1 printThread = new PrintThread1();
printThread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
// printThread.stop(true); --> 안정적이지 못한 방법 --> 비추
printThread.setStop(true);
}
}
public class InterruptExample {
public static void main(String[] args) {
Thread thread = new PrintThread2(); // 작업 스레드 생성
thread.start(); // 작업 스레드 실행
// 무한적으로 실행된다.
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
thread.interrupt(); // 작업 스레드에 interrupt() 실행한다.
}
}
[ 데몬 스레드 ]
- 주 main 스레드의 작업을 돕는 보조적인 역할을 수행하는 스레드
- 주 main 스레드가 종료되면 데몬 스레드는 강제적으로 자동 종료된다.
- 데몬 스레드의 역할 : 워드프로세서의 자동저장, 미디어플레이어의 동영상 및 음악 재생 등..
- 스레드를 데몬 스레드로 만드는 방법
: 주 스레드가 데몬이 될 스레드의 setDaemon(true) 를 호출한다.
반드시 start() 메서드 호출 전에 setDaemon(true) 를 호출해줘야한다!
--> 안그러면 IllegalThreadStateException 예외가 발생한다..
- 현재 실행중인 스레드가 데몬 스레드인지 아닌지 구분하는 방법
: isDaemon() 메서드를 실행시켜 리턴값이 true이면 데몬 스레드이다.
public class DaemonExample {
// 주(main) 스레드
public static void main(String[] args) {
AutoSaveThread autoSaveThread = new AutoSaveThread(); // <- 작업스레드 생성
autoSaveThread.setDaemon(true); // <- 작업스레드를 데몬스레드로 변경
autoSaveThread.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
System.out.println("메인 스레드 종료");
}
}
// 메인 스레드가 끝나도 작업 스레드가 안끝난게 하나라도 남아있다면 그 작업스레드가
// 끝날때까지 끝난게 아니다 (작업 스레드는 계속 실행된다).
// (--> 메인 스레드와 작업 스레드는 별개)
// 데몬스레드는 메인스레드와 연관되어서 메인 스레드가 끝나게되면 안끝난 작업스레드가
// 있어도 그냥 끝나게 해버린다.
// 따라서 위에서 작업스레드가 데몬스레드로 변경되어서 메인스레드가 끝나면 데몬스레드로
// 변경된 작업스레드가 끝나지 않았어도 그냥 끝낸다.
// 1초 단위로 SaveThread 실행
public class AutoSaveThread extends Thread {
public void save() {
System.out.println("작업 내용을 저장함.");
}
@Override
public void run() {
while(true) {
try {
Thread.sleep(1000); // 1초
} catch (InterruptedException e) {
break;
}
save();
}
}
}
[ 스레드 풀 ]
- 정의 : 스레드 폭증으로 일어나는 현상
- 병렬 작업 처리가 많아지면 스레드 개수가 증가한다.
- 스레드 생성과 스케줄링으로 인해 CPU가 바빠짐
- 메모리 사용량이 늘어남
- 애플리케이션의 성능 급격히 저하
* 스레드를 많이 늘려서 무작정 CPU를 많이 늘리는게 무조건 좋은건 아니다.
스레드가 폭증하면 위와 같은 현상들이 일어난다. 저러한 현상을 우리는 "스레드 풀" 이라고 한다.
스레드 풀을 어떻게 사용하느냐? 다음과 같다.
스레드 풀은 작업 처리에 사용되는 스레드를 일정 제한된 개수만큼 미리 생성하여
작업 큐(Queue)에 들어오는 작업들을 저장해놓고 하나씩 여유로운 스레드들에게 작업들을 하나씩
넣어준다. 작업 처리가 끝난 스레드는 작업 결과를 애플리케이션으로 전달하고 스레드는 다시
작업 큐에서 새로운 직업을 가져와서 처리한다.
이렇게 스레드 풀이 사용되면 메모리를 효율적으로 사용할 수 있으면서 속도도 좋아진다!! (Good!)
- 스레드 풀 사용 방법
: ExecutorService 인터페이스와 Executors 클래스를 사용해야 한다.
스레드 풀을 사용하고 생성하는 것은 java.util.concurrent 패키지에서 제공해준다.
Executors의 정적 메소드를 이용해서 ExecutorService 구현 객체를 생성한다.
스레드 풀 = ExecutorService 객체 ..(?)
ExecutorService가 동작하는 방식
'kh-정보교육원' 카테고리의 다른 글
25, 26, 27, 28일차_배운 내용(나는야 코생아 응애,,) (2) | 2022.12.09 |
---|---|
23,24일차_배운 내용(여전히 코생아) (2) | 2022.12.06 |
22일차(2)_배운 내용(어김없이 코생아) (0) | 2022.12.04 |
22일차(1)_배운 내용(어김없이 코생아) (1) | 2022.12.01 |