Thread
쓰레드에 대해 알기 전, 프로그램과 프로세스에 대해 간단하게 알아보자
프로그램 | 실행 안 된 상태 |
프로세스 | 실행된 프로그램. 운영체제로부터 시스템 자원을 할당받는 작업의 단위. 운영체제 대신 JVM에 스케줄링 요청을 통하여 쓰레드가 실행된다. |
스레드(thread)란?
스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미한다.
모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행한다.
또한, 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 한다.
단일쓰레드 | 멀티쓰레드 | |
설명 | 앞의 작업이 안되면 뒤에도 안된다. 하지만 동시에 시작하지 않기 때문에 한 개에 오류가 발생해도 다른 작업은 이상이 없다. ex) 매표소 |
하나의 처리 경로를 여러 개의 경로로 나누어 가질 수 있도록 한다. 특정 웹 서버는 멀티 쓰레드 응용프로그램이다. ex) 음식점 |
장점 | 안정적이다. 설계가 상대적으로 쉽다. |
효율성 증가 처리량 증가 처리비용 감소 |
단점 | 비효율적이다. 처리비용 증가. |
복잡하고 설계가 어려움 자원의 공유문제(동기화) 하나의 쓰레드 문제 발생 시 다 문제 발생 교착상태 |
교착상태(DeadLock)
멀티 쓰레드 중 쓰레드 간에 대기 상태가 종료되지 않아서 무한정 대기하는 비정상적인 상태이다.
- 오라클은 DB에서 교착상태가 발생하면 60초가 걸린다.
- 교착상태를 해결하기 위해서는 하나의 쓰레드를 없애거나, 전체 쓰레드를 깨워준다.
- 교착상태가 최대한 발생되지 않게끔 제어문과 예외처리 문법으로 꼼꼼히 설계해주어야만 한다.
멀티쓰레드의 생성과 실행
run() 메소드 정의 후 start()로 스케줄링을 해야한다.
- Thread 클래스 상속
Thread 클래스 안에 run() 메소드와 start() 메소드가 선언되어 있다.
- Runnable 인터페이스 지정
run() 메소드는 Runnable 인터페이스 안에 추상 메소드로 선언되며, start() 메소드는 선언되지 않는다.
생성해보자 !
Thread1 클래스
public class Thread1 extends Thread {
/*Thread 상속*/
private String data;
public Thread1(String data) {
super();
this.data = data;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(data);
/*실행 속도가 너무 빨라 sleep을 이용해 속도를 늦춘다*/
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
}
}
}
Thread2 클래스
public class Thread2 implements Runnable{
/*Runnable 인터페이스 지정*/
private String data;
public Thread2(String data) {
super();
this.data = data;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(data);
//보통 실시간 처리 때매 멀티 쓰레드를 구현하게 되는데,
//원하는 쓰레드를 종료시키고 싶어도 while true안에 로직이 작성되어 있기 때문에
//직접 break를 사용하는 방벅밖에 없다.
//만약 제어문으로 break를 사용하지 못한다면, 일부러 InterruptedException을 발생 시킨 후
//catch문에서 break를 사용하여 해당 쓰레드를 종료시킨다.
try {Thread.sleep(1000);} catch (InterruptedException e) {;}
}
}
}
Main
[단일 쓰레드]
Thread1 t1 = new Thread1("★");
Thread1 t2 = new Thread1("♥");
t1.run();
t2.run();
★
★
★
★
★
★
★
★
★
★
♥
♥
♥
♥
♥
♥
♥
♥
♥
♥
별의 출력이 모두 끝난 후 하트의 출력이 시작된다(단일쓰레드)
[멀티 쓰레드]
Thread1 t1 = new Thread1("★");
Thread1 t2 = new Thread1("♥");
/*start(): 해당객체에 재정의된 run 메소드를 스케줄링 해준다.*/
t1.start();
t2.start();
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
별과 하트가 번갈아가며 출력된다. (멀티쓰레드)
그렇다면 Runnable 인터페이스를 지정받은 Thread2 클래스로 start()메소드를 사용하는 방법은 무엇일까?
public class ThreadTest{
public static void main(String[] args) {
Thread1 t1 = new Thread2("!");
Thread2 t2 = new Thread2("?");
Thread thread1 = new Thread(t1);
Thread thread2 = new Thread(t2);
/*
Thread1은 Thread 클래스를 상속받았으므로 start()메소드가 사용가능하다
하지만 Thread2는 Runnable 인터페이스를 지정받아 start()메소드를 바로 사용할 수 없다
Thread 타입의 객체를 생성하고 그 객체에게 run 메소드를 가지고 있는 객체인 t1, t2를 전달한다.
Thread() 생성자는 Runnable 타입을 받을 수 있는 이유는? 업캐스팅
*/
thread1.start();
thread2.start();
Thread의 우선순위
쓰레드 실행이 종료된 후 "메인 쓰레드 종료"라는 문자열을 출력하고 싶을 때 아래와 같은 코드를
짰다고 생각해보자
Thread1 t1 = new Thread1("★");
Thread1 t2 = new Thread1("♥");
t1.start();
t2.start();
System.out.println("메인 쓰레드 종료");
하지만 결과는 예상과는 다르게 "메인 쓰레드 종료"가 먼저 출력되고 쓰레드가 실행되는 것을 확인할 수 있다.
메인쓰레드 종료
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
그 이유는 우선 순위 때문인데 , 이때 다른 쓰레드의 우선 순위를 main 쓰레드보다 높게 주고 싶을 때
join() 메소드를 사용하면 된다.
join() 메소드에서 예외(InterruptedException)가 발생할 수 있으므로, 예외처리를 해주어야한다.
Thread1 t1 = new Thread1("★");
Thread1 t2 = new Thread1("♥");
try {
t1.join();
t2.join();
} catch (InterruptedException e) {;}
System.out.println("메인쓰레드 종료");
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
★
♥
메인쓰레드 종료
이미지 출처 : https://eun-jeong.tistory.com/20
열심히 공부하고 있지만, 오류 사항이 존재 할 수 있습니다.
수정 사항이 존재 할 경우 알려주시면 감사하겠습니다 <(__)>
'프로그래밍 > JAVA' 카테고리의 다른 글
37 멀티쓰레드를 이용한 문제 풀이 (0) | 2021.05.04 |
---|---|
36 동기화(synchronized) (0) | 2021.05.04 |
34 Map 컬렉션 클래스(HashMap) (0) | 2021.04.29 |
33 Set 컬렉션 클래스(HashSet) (0) | 2021.04.29 |
댓글