[Java] Thread 란? (Thread 총 정리)
Process 와 Thread
먼저 프로세스(Process)
란 cpu에 의해 메모리에 올라가 실행중인 프로그램을 말한다.
자신만의 메모리 공간을 포함한 독립적인 실행공간을 갖고있다.
자바 JVM(Java Virtual Machine)은 주로 하나의 프로세스로 실행되며, 동시에 여러 작업을 수행하기 위헤 멀티 스레드를 지원한다.
Thread
란 프로세스 안에서 실질적으로 작업을 실행하는 단위를 말한다.
Java에서는 JVM에 의해 관리가 된다.
한 프로그램에 여러개의 스레드가 존재 가능하며, 스레드가 1개이면 단일 스레드, 2개 이상이면 멀티 스레드 환경이 된다.
Thread State
- Thread.State NEW : 스레드가 실행 준비가 완료된 상태, 스레드의 첫 시작.
- Thread.State RUNNABLE : 스레드가 실행 가능한 상태, 스레드가 대기열에서 실행을 기다리고 있음을 의미.
- Thread.State BLOCKED : 스레드가 차단되어 있는 상태, 스레드가 잠금(lock) 습득을 기다리는 상태.
- Thread.State WAITING : 스레드가 대기중인 상태, 대기 상태의 스레드는 다른 스레드가 작업을 완료하기를 기다리고 있는 상태이다.
- Thread.State TIMED_WAITING : 스레드가 정해진 시간동안 대기하는 상태, WAITING과의 차이는 정해진 시간동안 대기를 한다는 것이다.
- Thread.State TERMINATED : 스레드가 종료되거나 죽은 상태, 종료된 스레드는 실행이 완료되었음을 의미한다.
Java Thread class 와 Runnable interface
Java에서 Thread
를 생성하는 방법은 크게 두가지 이다.
하나는 Thread
클래스를 상속 받아 생성하는 방법이다. run() 메서드를 오버라이딩
다른 하나는 Runnable
인터페이스를 구현하여 생성하는 방법이 있다. run() 메서드 구현
보통 Runnable
인터페이스를 구현하는 방법으로 많이 생성하여 사용한다.
그 이유는 매우 간단하게, Thread
클래스를 extends
해버리게 되면, 다른 클래스를 더 이상 상속할 수 없기 때문이다.
단, Runnable
을 구현하여 스레드를 생성하는 경우, 객체 참조변수를 인자값으로 하는 Thread를 생성하여 사용해야 된다.
반면, java.lang.Thread
클래스를 상속받아 사용하는 경우 실행 스레드로 자신의 콜 스택을 가진 독립적인 프로세스가 된다.
Thread.Class Method
Method | Content | Exception |
---|---|---|
void sleep(long msec) | 지정된 millisecond 만큼 대기 | throws Interrupted Exception |
String getName() | 스레드 이름 반환 | |
void setName(String str) | 스레드 이름 str 지정 | |
void start() | 스레드 시작, run() 메서드 호출 | |
int getPriority() | 스레드 우선순위 반환 | |
void setPriority(int p) | 스레드 우선순위 p로 설정 | |
boolean isAlive() | 스레드가 시작되어 아직 끝나지 않았으면 return true, 끝났으면 return false | |
void join() | 스레드가 끝날 때 까지 대기 | throws Interupted Exception |
void run() | 스레드가 실행하는 함수 (오버라이딩 재정의) | |
void suspend() | 스레드를 일시정지 시킨다. | |
void resume() | 일시정지된 스레드를 다시 시작 | |
void yield() | 다른 스레드에게 실행 상태 양보 후, 준비 상태로 변환 | |
void notify() | Wait 상태에 있는 스레드를 Runnable 상태로 만듬 | |
Thread currentThread() | 현재 실행중인 스레드를 반환 | |
Thread.State getState() | 스레드의 상태 반환 | |
void interrupt() | 스레드를 중단 | |
boolean isInterrupt() | 스레드가 중단 되었는지 boolean 반환 |
Java Thread 사용하기
Extends Thread.class
package thread;
import java.lang.Thread;
class MyThread extends Thread{
int loopCount;
public MyThread() {
this.loopCount = 0;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println("Thread getName() : " + Thread.currentThread().getName() + " loopCount : " + loopCount);
loopCount++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
}
}
// 출력
/*
Thread getName() : Thread-0 loopCount : 0
Thread getName() : Thread-0 loopCount : 1
Thread getName() : Thread-0 loopCount : 2
Thread getName() : Thread-0 loopCount : 3
Thread getName() : Thread-0 loopCount : 4
*/
Implement Runnable.interface
package thread;
import java.lang.Thread;
class MyThread implements Runnable{
int loopCount;
public MyThread() {
this.loopCount = 0;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println("Thread getName() : " + Thread.currentThread().getName() + " loopCount : " + loopCount);
loopCount++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread obj = new MyThread();
Thread thread = new Thread(obj);
thread.start();
}
}
// 출력
/*
Thread getName() : Thread-0 loopCount : 0
Thread getName() : Thread-0 loopCount : 1
Thread getName() : Thread-0 loopCount : 2
Thread getName() : Thread-0 loopCount : 3
Thread getName() : Thread-0 loopCount : 4
*/
위에 코드처럼, Thread class extends
하느냐, 혹은 Runnable interface 를 implement
하느냐, 두가지의 차이로 Thread를 생성하여 사용할 수 있다.
Thread.sleep()
메서드를 이용하여 1초 단위로 출력되게 만들 수 있다.
두 개의 Thread
package thread;
import java.lang.Thread;
class MyThread implements Runnable{
int loopCount;
int millisecond;
public MyThread(int millisecond) {
this.loopCount = 0;
this.millisecond = millisecond;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println("Thread getName() : " + Thread.currentThread().getName() + " loopCount : " + loopCount);
loopCount++;
try {
Thread.sleep(this.millisecond);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread obj1 = new MyThread(3000);
MyThread obj2 = new MyThread(5000);
Thread thread1 = new Thread(obj1);
Thread thread2 = new Thread(obj2);
thread1.start();
thread2.start();
}
}
// 출력
/*
Thread getName() : Thread-0 loopCount : 0
Thread getName() : Thread-1 loopCount : 0
Thread getName() : Thread-0 loopCount : 1
Thread getName() : Thread-1 loopCount : 1
Thread getName() : Thread-0 loopCount : 2
Thread getName() : Thread-0 loopCount : 3
Thread getName() : Thread-1 loopCount : 2
Thread getName() : Thread-0 loopCount : 4
Thread getName() : Thread-1 loopCount : 3
Thread getName() : Thread-1 loopCount : 4
*/
두개의 스레드를 생성하여 만들어 본 예제 코드이다.
여기서 볼 점은 Thread.sleep()
의 millisecond를 다르게 주었다는 것이다.
즉, 대기시간이 서로 상이하기 때문에, 직접 실행하게 되면 조금씩 두번째 스레드가 밀리게 된다.
출력값을 볼 때 스레드의 이름과 loopCount를 매칭해서 보면 이해가 된다.
참고로, Thread.sleep()
메서드에 음수의 값을넣게 되면 java.lang.IllegalArgumentException: timeout value is negative
라는 Exception이 나오게 된다.
Thread join()
package thread;
import java.lang.Thread;
class MyThread implements Runnable{
int loopCount;
int millisecond;
public MyThread(int millisecond) {
this.loopCount = 0;
this.millisecond = millisecond;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println("Thread getName() : " + Thread.currentThread().getName() + " loopCount : " + loopCount);
loopCount++;
try {
Thread.sleep(this.millisecond);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread obj1 = new MyThread(3000);
MyThread obj2 = new MyThread(5000);
Thread thread1 = new Thread(obj1);
Thread thread2 = new Thread(obj2);
thread1.start();
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
thread2.start();
}
}
// 출력
/*
Thread getName() : Thread-0 loopCount : 0
Thread getName() : Thread-0 loopCount : 1
Thread getName() : Thread-0 loopCount : 2
Thread getName() : Thread-0 loopCount : 3
Thread getName() : Thread-0 loopCount : 4
Thread getName() : Thread-1 loopCount : 0
Thread getName() : Thread-1 loopCount : 1
Thread getName() : Thread-1 loopCount : 2
Thread getName() : Thread-1 loopCount : 3
Thread getName() : Thread-1 loopCount : 4
*/
이번에는 Thread.join()
메서드를 사용한 것이다.join()
메서드는 스레드가 끝날 때 까지 대기시키는 메서드이다.
즉, 메인 메서드에서, thread1.join()
을 통해 thread1이 끝날 때 까지 그 뒤의 스레드는 실행되지 않고,
대기상태에 놓이게 되고, thread1이 실행이 끝나게 되면 thread2가 실행되게 된다.
Thread priority
package thread;
import java.lang.Thread;
class MyThread implements Runnable{
int loopCount;
int millisecond;
public MyThread(int millisecond) {
this.loopCount = 0;
this.millisecond = millisecond;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println("Thread getName() : " + Thread.currentThread().getName() + " loopCount : " + loopCount);
loopCount++;
try {
Thread.sleep(this.millisecond);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread obj1 = new MyThread(1000);
MyThread obj2 = new MyThread(1000);
Thread thread1 = new Thread(obj1);
Thread thread2 = new Thread(obj2);
thread1.setPriority(3);
thread2.setPriority(10);
thread1.start();
thread2.start();
}
}
// 출력
/*
Thread getName() : Thread-1 loopCount : 0
Thread getName() : Thread-0 loopCount : 0
Thread getName() : Thread-1 loopCount : 1
Thread getName() : Thread-0 loopCount : 1
Thread getName() : Thread-1 loopCount : 2
Thread getName() : Thread-0 loopCount : 2
Thread getName() : Thread-1 loopCount : 3
Thread getName() : Thread-0 loopCount : 3
Thread getName() : Thread-1 loopCount : 4
Thread getName() : Thread-0 loopCount : 4
*/
이번에는 setPriority()
메서드를 통해 우선순위를 설정해 줬다.
그런후 sleep()
의 타임을 동일하게 설정해 줬다.
이렇게 할 경우, 우선순위가 더 높은 스레드가 먼저 실행이 되는 결과를 볼 수 있다.
Thread Interrupt
package thread;
import java.lang.Thread;
class MyThread implements Runnable{
int loopCount;
int millisecond;
Thread thread;
public MyThread(int millisecond) {
this.loopCount = 0;
this.millisecond = millisecond;
this.thread = new Thread();
}
public void start(){
this.thread = new Thread(this);
this.thread.run();
}
@Override
public void run() {
System.out.println(this.thread.getState());
while(!this.thread.isInterrupted()){
try{
if(this.loopCount == 5){
this.thread.interrupt();
System.out.println("Thread interrupt!");
return;
}
Thread.sleep(this.millisecond);
System.out.println("Thread getName() : " + Thread.currentThread().getName() + " loopCount : " + this.loopCount);
System.out.println(Thread.currentThread().getState());
this.loopCount++;
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
public class Test {
public static void main(String[] args) {
MyThread obj1 = new MyThread(1000);
obj1.start();
}
}
// 출력
/*
NEW
Thread getName() : main loopCount : 0
RUNNABLE
Thread getName() : main loopCount : 1
RUNNABLE
Thread getName() : main loopCount : 2
RUNNABLE
Thread getName() : main loopCount : 3
RUNNABLE
Thread getName() : main loopCount : 4
RUNNABLE
Thread interrupt!
*/
이번에는 코드를 좀 변경시켜 봤다.
MyThread 라는 클래스내에 Thread 객체를 생성시켜, 실행시켜보았다.
loopCount가 5가 되면 interrupt()
메서드를 사용하여 스레드를 강제 종료시키게 하였다.
위의 방식처럼 스레드를 만들어서 자유자재로 사용하게 된다면, 객체 내에 다양한 로직들을 추가하여
스레드를 사용할 수 있게 될 것 같다.
'1.프로그래밍 > Java' 카테고리의 다른 글
[Spring Boot] Spring Data JPA 기초(코드로 배우는 스프링 부트 웹 프로젝트 ) (2) | 2022.09.25 |
---|---|
[Java] Java Synchronized 란? (Java 동기화) (0) | 2022.09.11 |
[Java] Factory Method Pattern 이란? (디자인 패턴, 간단한 예제) (1) | 2022.07.30 |
[Java] Singleton Pattern 이란? (디자인 패턴) (0) | 2022.07.28 |
[Java] BufferReader, BufferWriter 사용하기(System.out.print 와 Scanner 가 느린 이유) (0) | 2022.07.05 |