기본 소개

  • Java에서는 경합 상태일 때 데이터의 정합성을 보장하기 위해 스레드 동기화로 한 번에 하나의 스레드만 공유 자원에 접근할 수 있도록 한다. 이를 구현하기위해 Java에서는 각 인스턴스들이 자신의 Monitor를 가지게하고, 스레드가 이 인스턴스를 사용할 때 Monitor를 점유하도록 한다. 이 Monitor는 하나의 스레드만 점유할 수 있기때문에, 다른 스레드들은 Wait Queue에서 기다리게 된다. (synchronized 키워드를 사용하면 점유하게된다)
  • Java 스레드에는 ‘데몬 스레드(daemon thread)’와 ‘일반 스레드(non-daemon thread)’가 있다.
    • 데몬 스레드: 백그라운드로 실행되고, 일반 스레드가 모두 종료되면 데몬 스레드의 의미가 사라지기 때문에 강제적으로 자동 종료된다. 대표적으로 가비지 컬렉션, 화면 자동갱신 등이 있다.
    • 일반 스레드: 사용자가 직접 생성한 스레드로 대표적으로 `static void main(String[] args)’가 실행되는 스레드가 일반 스레드다.
  • 우선순위
    • Java는 스레드 스케줄링에 우선순위 방식과 라운드 로빈 방식을 사용한다. 즉, 우선순위가 높은 스레드가 더 많은 실행을 할 수 있도록 스케줄링한다. 우선순위는 1~10으로 1이 가장 낮은 우선순위고, 10이 가장 높다.

구현하기

Thread 클래스를 상속해서 실행하는 방법이 있다.

public class NewThread extends Thread {
    public void run() {
        long startTime = System.currentTimeMillis();
        int i = 0;
        while (true) {
            System.out.println(this.getName() + ": New Thread is running..." + i++);
            try {
                //Wait for one sec so it doesn't print too fast
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ...
        }
    }
}
public class SingleThreadExample {
    public static void main(String[] args) {
        NewThread t = new NewThread();
        t.start();
    }
}

하지만 이 방법은 스레스 생성과 제거 비용이 상당히 크기 때문에 별도의 스레드풀을 만들어 둬야된다.

ExecutorService executor = Executors.newFixedThreadPool(10);
...
executor.submit(() -> {
    new Task();
});

이때, execute 메소드는 아무 반환 값이 없고, submit 은 실행 결과를 Future로 반환한다.