스레드 설명에 앞서 프로세스는 무엇일까? 프로세스는 독립적인 실행 단위이고, 스레드는 프로세스 내에서 실행되는 실행흐름이다. 스레드는 프로세스 내에서 동시에 실행될 수 있으므로 프로그램의 성능을 향상시키는 데 사용할 수 있음.
1.Runnable 인터페이스를 상속한 클래스 만들기 (일반적으로 이 방법을 사용)
2. Thread 클래스를 상속한 클래스를 만들기
⭐ 둘다 run() 메소드를 오버라이드 해야 함
public class Main {
public static void main(String[] args) {
Runnable print100 = new PrintNum(100);
Thread thread1 = new Thread(print100);
Thread thread2 = new Thread(print100);
thread1.start();
thread2.start();
}
}
class PrintNum implements Runnable{
private int lastNum;
public PrintNum(int n){
lastNum = n;
}
@Override
public void run(){
for (int i = 0; i <= lastNum; i++) {
System.out.println(" " + i);
}
}
}
Runnable 인터페이스를 구현한 클래스를 만들기 Runnable 인터페이스를 구현한 클래스를 만들고, 이 클래스의 인스턴스를 Thread 클래스의 생성자에 전달하여 Thread 객체를 만든다. 이때, Thread 객체를 start() 메서드로 시작시키면 Runnable 인터페이스의 run() 메서드가 호출되어 작업 스레드가 실행된다.
public class Main {
public static void main(String[] args) {
PrintChar thread1 = new PrintChar('A', 10);
PrintChar thread2 = new PrintChar('B', 10);
thread1.start();
thread2.start();
}
}
class PrintChar extends Thread{
private char charToPrint;
private int times;
public PrintChar(char c, int t){
charToPrint = c;
times = t;
}
@Override
public void run(){
for (int i = 0; i < times; i++) {
System.out.println(i);
}
}
}
PrintChar 클래스
문자 하나와 숫자 하나를 인자로 받아서, 해당 문자를 숫자만큼 반복해서 출력하는 스레드이다.
❗ 주의 : Thread를 실행할 때 start()와 run() 의 차이??
run()을 호출하면 생성된 스레드 객체를 실행하는 것이 아니라, 단순 스레드 클래스 내부의 run 메소드를 실행시킨다. 따라서 main 함수의 스레드를 그대로 사용해서 run 메소드를 실행하기 때문에 새로운 스레드가 생기지 않고 병렬처리가 불가능하다.
하지만 start()는 새로운 스레드를 실행하는데 필요한 call stack을 생성한 다음에 run()을 호출해, 생성된 call stack에 run()이 첫번째로 저장되게 한다. 즉, start()를 호출하면 스레드를 새롭게 생성해서 해당 스레드를 runnable 한 상태로 만든 후 run() 메소드를 실행하게 된다.
결론은 start()를 호출해야만 멀티스레드로 병렬 처리가 가능하다!!!
⭐ 그러면 왜 추상 메서드로 run()밖에 존재하지 않는 Runnable은 왜 사용하는 것일까?
Thread를 바로 사용하려면 상속을 받아야 한다. Java는 다중 상속을 허용하지 않기 때문에 Thread 클래스를 바로 상속받는 경우 다른 클래스를 상속받지 못한다. 그러나 Runnable 인터페이스를 구현한 경우에는 다른 인터페이스를 추가로 구현할 수 있을 뿐만 아니라, 다른 클래스도 상속받을 수 있다.
따라서 클래스의 확장성이 중요하다면 Runnable 인터페이스를 구현해 Thread에 주입하는 방식이 좋을 수 있다.