프로세스는 프로그램이 메모리를 할당받아 실행 중인 상태를 의미하고, 데이터와 메모리 등의 자원과 스레드로 구성된다.
하나의 프로그램에서 여러 개의 프로세스가 생길 수가 있다. 같은 프로그램을 여러번 실행하면 실행한 횟수 만큼 프로세스가 생기는 것이다.
스레드는 프로세스 내에서 실제로 작업을 수행하는 주체를 의미한다. 모든 프로세스에는 적어도 하나의 스레드가 존재하여 작업을 수행한다.
하나의 프로세스 내에서 여러 개의 스레드를 사용하는 경우를 멀티 스레드(multi-threaded) 프로세스라 한다. 멀티 프로세스(multi-process)는 여러 개의 CPU를 사용하여 여러 프로세스를 동시에 실행하는 것을 말한다.
멀티 스레드와 멀티 프로세스 모두 여러 흐름을 동시에 수행한다는 공통점이 있다. 멀티 프로세스는 각 프로세스가 독립적인 메모리를 가지고 별도로 실행되지만, 멀티 스레드는 각 스레드가 자신이 속한 프로세스의 메모리를 공유한다는 차이가 있다.
컴퓨터에서 동시에 처리할 수 있는 최대 작업 수는 CPU의 코어 수이다. 코어 수보다 더 많은 스레드가 실행될 경우 각 코어가 정해진 시간 동안 여러 작업을 번갈아가며 수행한다.
Context Switching이란 코어에서 스레드가 교체될 때 발생하는 것으로, 현재까지의 작업 상태나 다음 작업에 필요한 각종 데이터를 저장하고 읽어오는 작업을 말한다. Context Switching에 시간이 오래 걸릴수록, 멀티 스레딩의 효율이 저하된다. 스레드를 많이 실행하는 것이 언제나 효율적인 게 아니라는 것을 의미한다.
모든 Java 프로그램은 JVM의 메인 스레드가 main 메서드를 실행하면서 시작된다. main의 첫 코드부터 아래로 순차적으로 실행하며, 마지막 코드를 실행한 이후나 return문을 만날 경우 종료된다.
메인 메서드는 작업 스레드를 만들어서 병렬로 코드를 실행할 수 있다. 메인 스레드가 종료되었다고 해서, 작업 스레드들이 종료되지는 않는다.(단, 데몬 스레드의 경우 메인 스레드가 종료되면 자신도 종료된다.) 메인 스레드 뿐만 아니라 다른 작업 스레드들이 모두 종료되어야 프로세스가 종료되는 것이다.
하나의 코어에서 멀티 스레드가 번갈아 가며 실행하는 성질을 말한다. 각 코어마다 한 번에 하나의 스레드만 실행할 수 있기 때문에 가지는 성질이다.
각 코어에서 스레드를 동시에 실행하는 성질을 말한다. 여러 코어가 스레드를 하나씩 맡아서 독립적으로 실행하는 것으로, 서로에게 방해를 받지 않는다.
스레드는 Runnable 인터페이스를 구현하거나, Thread 클래스를 상속받음으로써 생성할 수 있다. 모두 run() 메서드를 오버라이딩하여 구현하면 된다.
다른 클래스를 상속받은 클래스를 스레드로 만들려는 경우 이중 상속이 불가능하므로 Runnable 인터페이스를 구현하여 해결해야 한다.
public class Task extends Thread{
@Override
public void run() {
// 실행할 코드
}
}
public class Main {
public static void main(String[] args) {
Thread task = new Task();
task.start();
}
}
public class Task implements Runnable{
@Override
public void run() {
// 실행할 코드
}
}
public class Main {
public static void main(String[] args) {
Runnable task = new Task();
Thread thread = new Thread(task);
thread.start();
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 실행 코드
}
});
thread.start();
}
}
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
// 실행 코드
});
thread.start();
}
}
스레드가 코어 수보다 많을 경우 어떤 순서로 동시성으로 실행할 것인지를 결정하는 것을 말한다. 이 스케줄링에 의해 스레드들은 번갈아 가며 run() 메서드를 조금씩 실행한다.
우선순위가 높은 스레드가 실행 상태를 더 많이 가져가도록 스케줄링하는 방식을 말한다. 개발자가 각 스레드의 우선순위를 설정함으로써 제어할 수 있다.
시간 할당량(Time slice)을 정하여 CPU가 하나의 스레드를 정해진 시간 만큼 실행하는 방식이다. 개발자가 코드로 제어할 수 없다.
각 스레드는 우선순위에 관한 필드를 가진다. 이 우선순위에 따라 특정 스레드가 더 많은 시간동안 작업을 하도록 설정할 수 있다.
필드 | 설명 |
---|---|
static int MAX_PRIORITY | 스레드가 가질 수 있는 최대 우선순위를 명시함. |
static int MIN_PRIORITY | 스레드가 가질 수 있는 최소 우선순위를 명시함. |
static int NORM_PRIORITY | 스레드가 생성될 때 가지는 기본 우선순위를 명시함. |
스레드의 우선순위의 범위는 1 ~ 10으로, getPriority()와 setPriority() 메서드를 통해 우선순위에 접근할 수 있다. 이 우선순위는 비례적인 절댓값이 아닌 상댓값이다.
다음 예시와 같이 하나의 스레드의 우선순위를 10으로 설정하여 실행하자.
class ThreadWithRunnable implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName());
// 현재 실행 중인 스레드의 이름을 반환함.
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Thread02 {
public static void main(String[] args){
Thread thread1 = new Thread(new ThreadWithRunnable());
Thread thread2 = new Thread(new ThreadWithRunnable());
① thread2.setPriority(10); // Thread-1의 우선순위를 10으로 변경함.
② thread1.start(); // Thread-0 실행
③ thread2.start(); // Thread-1 실행
System.out.println(thread1.getPriority());
System.out.println(thread2.getPriority());
}
}
"Thread-1"과 "Thread-0"이 번갈아 출력된다. thread2가 우선순위에 비례하여 thread1보다 10배 빨리 실행되는 것이 아니다. 단지 thread2의 우선순위가 더 높기 때문에 thread2의 start가 다음에 호출되어도 실행이 먼저 되는 것일 뿐이다.
스레드의 경우 개발자가 이름을 따로 설정하지 않는 이상 자동으로 이름이 설정되는데, 메인 스레드의 경우 main, 그 외의 스레드의 경우 Thread-n으로 설정된다. 스레드의 이름을 받을 경우 getName(), 이름을 설정할 경우 setName() 메서드를 호출하면 된다.
public class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class Main {
public static void main(String[] args) {
Thread task = new Thread(new Task());
task.start(); // Thread-0
}
}
현재 실행중인 스레드 경우 정적 메서드인 currentThread() 통해 반환받을 수 있다.
public class Main {
public static void main(String[] args) {
Thread current = Thread.currentThread();
System.out.println(current.getName()); // main
}
}
http://tcpschool.com/java/java_thread_concept
http://tcpschool.com/java/java_thread_multi