
프로세스 일꾼
IDE의 run만 누르면 프로그램이 실행되니까 프로그램이 어떻게 시작하는지 신경쓰지 않는 경우가 많다.
사실 실행을 누르는 순간 프로그램은 OS에 의해 프로세스가 되고 CPU는 그 프로세스의 자원을 할당받는다.
쓰레드는 프로세스 내에서 일하는 일꾼이라고 보면 된다.
정리하자면 동작하는 프로그램 = 프로세스. 프로세스의 동작 단위 = 쓰레드
일꾼이 하나이면 싱글 쓰레드, 다수면 멀티 쓰레드라고 한다.
지금까지의 자바 프로그램은 main()메서드에서 run을 누르고 순서대로 하나의 흐름으로 동작했다. 이걸 싱글 쓰레드로 동작하고 있던거다.
심지어 우리는 쓰레드의 구현조차 하지않아서 어떻게 프로세스가 동작하는지 알 수 조차 없었다. 하지만 지금까지 계속 main()메서드의 쓰레드인 메인 메서드를 이용해서 프로세스를 동작시켰고, 자바에서 메인 메서드는 대장 메서드라, 이게 종료되면 JVM이 종료된다.
하지만, 우리가 쓰레드를 생성하고 여러개를 사용하기 시작하면 멀티 쓰레드가 된다. 쓰레드끼리는 자원을 동유해서 여러개의 흐름을 만들 수 있다. 즉, 동시 작업이 가능해서 성능이 좋아진다.
다만, 동기화 문제와 데드락 문제가 발생할 위험이 있다.
가장 기본적인 방식이다. 클래스 파일을 만들듯이 쓰레드를 구현할 수 있는데, 다음과 같다.
public class 쓰레드명 extends Thread {
@Override
public void run(){
로직
}
}
psvm(){
쓰레드명 thread1 = new 쓰레드명();
thread1.start();
}
이렇게 Thread 클래스를 extends해서 사용하는 방법이 있고, 다른 방법이 또 있다.
public class 쓰레드명 implements Runnable{
@Override
public void run(){
로직
}
}
이 방법은 Runnable 인터페이스를 상속받아서 구현하게 되는데, 아래 방법을 더 많이 사용한다.
이유는 인터페이스는 다중 상속을 지원해서 extends보다 확장성이 좋기 때문.
클래스로 만들 수 있지만, 람다식으로도 만들 수 있다.
public class Main(){
psmv(){
Runnable task = ()->{로직};
}
Thread task1 = new Thread(task,"쓰레드1");
task1.start();
}
이렇게 추가적인 class 파일을 생성하지 않고 람다식으로 바로 구현이 가능하기도 하다.
위에서 생성한 모든 쓰레드가 사용자 쓰레드다.
즉, 메인 쓰레드, task1... 다 사용자 쓰레드다.
foreground에서 실행되며 우선순위가 상대적으로 높은 쓰레드다.
background에서 동작하는 상대적으로 낮은 우선순위의 쓰레드다.
사용자 쓰레드가 메인 로직을 처리하는 느낌이면 데몬 쓰레드는 보조적인 역할을 담당한다. 대표적으로 가비지 콜렉터가 데몬 쓰레드이다.
Runnable task =()->{};
Thread task1 = new Thread(task,"데몬");
task1.setDaemon(true);
위의 SetDaemon(true) 을 통해서 데몬 쓰레드로 설정이 가능하다.
쓰레드는 병렬적으로 움직이나.. 실행 log를 찍어보면 A -> B -> A 하면서 동작은 1개의 쓰레드가 하고 계속 실행 쓰레드를 스왑하면서 log가 찍히는 걸 볼수있다.
그래서 작업의 중요도에 따라 쓰레드의 우선순위를 높게 줘서 처리를 더 빠르게 해달라는 요청이 가능하다.
Runnable task =()->{};
Thread task1 = new Thread(task,"1Th");
Thread task2 = new Thread(task,"2Th");
task1.setPrioirty(10); //가장 높은 우선순위
task2.setPrioirty(1); //가장 낮은 우선순위
다만 우선순위가 높다고 무조건 task1가 먼저 종료되는건 아니다. 먼저 종료될 확률을 높이는 것 뿐..
실제로 실행해보면 task2가 먼저 종료되는 경우도 발생한다.
쓰레드는 기본적으로 그룹에 포함되어 있다. 한 그룹에 묶이는 쓰레드는 서로 관련이 있는 쓰레드이다.
메인 쓰레드는 main 그룹에 포함되고, 나머지 사용자 쓰레드는 지정이 가능하다.
모든 쓰레드들은 반드시 하나의 그룹에 포함되어 있어야하는데, 생성시 그룹이 지정되지 않은 쓰레드들은 자동으로 main 그룹에 포함된다.
ThreadGroup group1 = new ThreadGroup("G1");
Thread task1 = new Thread(G1, task, "쓰레드1");
sout(task1.getThreadGroup().getName());
이런식으로 그룹을 생성하고, Thread의 매개변수 1번에 그룹명을 지정해서 해당 그룹에 속하는 쓰레드를 생성할 수 있다.
그룹으로 묶인 쓰레드들은 G1.interrupt();등을 통해 한번에 관리될 수 있다.