스레드의 다양한 생성 방법과 스레드를 연결하는 멀티 스레드를 사용하는 법 및 스레드 간 데이터 공유에 대해 글을 작성해 보겠습니다.
스레드 생성
스레드는 메인 스레드가 종료되어도 실행되고 있는 스레드와 메인 스레드 배경에서 실행되는 데몬 스레드가 있습니다.
즉 일반 스레드는 메인 스레드의 종료를 막고, 데몬 스레드는 막지 않습니다.
익명 클래스사용
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello thread : " + Thread.currentThread().getName());
}
});
thread.start();
람다식 사용
Thread thread =
new Thread(() -> System.out.println("Hello thread : " + Thread.currentThread().getName()));
thread.start();
상속 사용
private static class NewThread extends Thread {
@Override
public void run() {
System.out.println("Hello thread : " + Thread.currentThread().getName());
}
}
private static class NewThread2 implements Runnable {
@Override
public void run() {
System.out.println("Hello thread : " + Thread.currentThread().getName());
}
}
Thread thread = new NewThread();
Thread thread2 = new Thread(new NewThread2());
thread.start();
thread2().start();
스레드의 종료
interrupt : CPU의 정상적인 프로그램 실행을 방해했다는 의미입니다.
여기서 Thread 또한 interrupt() 메서드를 호출하여 종료시킬 수 있습니다.
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new NewThread());
thread.start();
}
private static class NewThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(500000);
} catch (InterruptedException e) {
System.out.println("Exiting blocking thread");
}
}
}
}
기본적으로 위의 코드는 메인 스레드가 종료되었음에도 불구하고 애플리케이션이 종료 되지 않습니다.
왜냐하면 새로 생성된 쓰레드가 작동되고 있기 때문인데요.
여기서 thread.interrupt(); 메서드를 실행하면 종료됩니다.
thread.interrupt();
그러면 매번 위와 같이 try / catch 구문으로 감싸주어야 할까요?
여기서 외부에서 인터럽트 명령어를 실행했는지 알 수 있는 방법이 있습니다.
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new NewThread());
thread.start();
thread.interrupt();
}
private static class NewThread implements Runnable {
@Override
public void run() {
System.out.println("Start Thread");
loop();
}
public void loop() {
while (true) {
System.out.println("Looping");
}
}
}
}
위의 코드는 interrupt()를 호출해도 무한루프를 돌다가 프로그램이 뻗게 되어 있습니다.
다음 코드를 추가해 줍니다.
public void loop() {
while (true) {
System.out.println("Looping");
if (Thread.currentThread().isInterrupted()) {
System.out.println("Interrupted");
break;
}
}
}
이제 위와 같이 정상적으로 스레드가 종료되는 것을 확인할 수 있습니다.
데몬 스레드 (Daemon Thread)
예를 들어 메모장에서 10초마다 자동으로 저장되는 스레드가 있다고 가정한다.
만약 메모를 하다가 메모장을 꺼버렸을 때 이 메모장이 종료가 되지 않고 계속 떠있는 상태라면 엄청난 오류라고 생각합니다.
이럴 때 데몬 스레드를 사용하여 메모장이 닫힐 때(메인 스레드가 종료될 때) 같이 종료되는 스레드를 사용하면 됩니다.
데몬 스레드의 생성
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new NewThread());
thread.setDaemon(true);
thread.start();
}
thread.setDaemon(true); 를 통해 설정할 수 있습니다. 기본 값은. false입니다.
스레드의 연결
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new NewThread());
Thread thread2 = new Thread(() -> System.out.println("Start Thread2"));
thread.start();
thread2.start();
System.out.println("Start Main");
}
private static class NewThread implements Runnable {
@Override
public void run() {
System.out.println("Start Thread1");
}
}
}
위와 같은 코드가 있을 때 기본적으로 스레드는 순서가 없어 출력되는 순서가 매번 다르게 나옵니다.
여기서 그러면 스레드 1과 스레드 2가 실행된 뒤에 메인 메서드가 실행되게 하려면 어떻게 할까요?
Thread의 join() 메서드를 사용하면 됩니다.
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new NewThread());
Thread thread2 = new Thread(() -> System.out.println("Start Thread2"));
thread.start();
thread2.start();
thread2.join();
thread.join();
System.out.println("Start Main");
}
private static class NewThread implements Runnable {
@Override
public void run() {
System.out.println("Start Thread1");
}
}
}
스레드 간의 데이터 공유
객체는 레퍼런스가 최소 하나라도 있으면 힙에 머무르는데 모든 레퍼런스가 없으면 가비지 컬렉터에 의해 사라집니다.
멤버 변수는 부모 객체와 묶이는데 부모 객체와 같은 생명 주기를 갖습니다.
정적 변수는 애플리케이션 실행되는 내내 가만히 그 자리에 머뭅니다.
Object obj1 = new Object();
Object obj2 = obj1;
위의 코드에서 new Object();는 오브젝트이고 obj1과 obj2는 new Object();의 주소값을 가지는 레퍼런스입니다.
따라서 레퍼런스가 메서드 내에서 선언되면 레퍼런스는 스택에 할당됩니다.
그리고 클래스의 멤버일 경우는 부모 객체와 함께 힙 상단에 할당됩니다.
객체는 항상 힙에 할당됩니다.
각 영역에 할당되는 것들
Heap | Stack |
Objects | Local primitive types |
Class members | Local references |
Static variables |
힙영역은 공유가 되고 스택영역은 스레드 간 공유가 안된다.
'Java > Thread' 카테고리의 다른 글
Thread (4) - 락킹 기법과 데드락 (0) | 2023.11.09 |
---|---|
Thread (3) - 스레드 간 데이터 공유의 문제점 및 해결 방법 (0) | 2023.11.07 |
Thread (1) - 운영체제 관점에서의 멀티 스레드 (0) | 2023.11.07 |
댓글