두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우에는 싱글 쓰레드 프로세스보다 멀티쓰레드 프로세스가 더 효율적이다.
예를 들어 사용자의 입력이 필요한 경우 싱글 쓰레드라면 입력을 기다리는 동안 다른 작업을 할 수 없다. 그러나 멀티쓰레드라면 입력을 기다리는 동안 다른 쓰레드가 작업을 할 수 있다.
쓰레드가 입출력(I/O) 처리를 위해 기다리는 것을 I/O blocking 이라고 한다.
//1. 상속
class MyThread extedns Thread {
@Override
public void run (){}
}
MyTread thread = new MyThread();
thread.start(); //실행
//2.인터페이스
class MyThread implements Runnable {
@Override
public void run (){}
}
Runnable r = new MyThread();
Tread thread2 = new Thread(r);
thread2.start(); //실행
start()
로 쓰레드를 실행시키면 실행대기에 들어간 후 차례가 되면 실행된다.
만약 실행 중인 쓰레드가 없다면 바로 실행된다.
또한 한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다.
→ 하나의 쓰레드에 대해 start
한 번만 호출 가능
만약 쓰레드의 작업을 한 번 더 수행해야 한다면 새로운 쓰레드를 생성하고, 다시 start
를 호출해야한다.
쓰레드는 우선 순위(priority)라는 속성을 가지고 있다.
이 우선 순위의 값에 따라 쓰레드가 얻는 실행 시간이 달라진다.
우선 순위 범위는 1-10이며 숫자가 높을 수록 우선 순위가 높다.
메인 쓰레드의 디폴트 값은 5이다.
Thread t1 = new Thread();
t1.setPriority(7);
t1.getPriority();
단, 자바는 쓰레드가 우선 순위에 따라 처리되는 로직이 강제되지 않기 때문에 JVM마다 실행 결과가 다를 수 있다.
자신이 속한 쓸드 그룹이나 하위 그룹은 변경할 수 있지만 다른 쓰레드 그룹의 쓰레드는 변경하지 못한다.
Thread(TreadGroup group, String name)
우리가 생성하는 모든 쓰레드 그룹은 main쓰레드 그룹의 하위 쓰레드 그룹이 되며, 그룹을 지정하지 않고 생성한 쓰레드는 자동적으로 main쓰레드에 속한다.
Tread t = new Thread();
t.setDaemon(true); // 반드시 start() 전에 실행되어야 함
t.start();
🔎 상태 설명
- new : 쓰레드가 생성되고
start()
가 호출되지 않은 상태- runnalble : 실행 중 or 실행 가능한 상태
- blocked : lock이 풀릴 때 까지 기다리는 상태 > 동기화 블럭에 의해 발생
- wating : unrunnable 한 일시 정지 상태
- terminated :쓰레드 작업이 종료된 상태
start()
가 호출되면 우선 실행대기열(Queue)에 저장되어 자신의 차례를 기다린다.yield()
를 만나면 다시 실행 대기 상태가 되고, 다음 차례의 쓰레드가 실행된다.stop()
이 호출되면 쓰레드는 소멸된다.try {
Thread.sleep(1000); // 1초 멈춤
} catch(InterruptedException e) {...}
sleep()
에 의해 일시정지 된 쓰레드는 interrupt()
가 호출되면 InterruptedException
이 발생하여 잠에서 깨어나 실행 대기 상태가 된다.
그래서 항상 try-catch
문으로 예외 처리를 해줘야한다.
진행 중인 쓰레드의 작업이 끝나기 전에 취소 시켜야할 때 사용한다.
단지 멈추라고 요청하는 것일 뿐 쓰레드를 강제 종료시키지는 못한다.
Thread t = new Thread();
t.start();
...
t.interrupt();
interrupted()
: 현재 쓰레드의 interrupted 상태를 반환 후 , false로 변경isInterrupted()
:쓰레드의 interrupted 상태를 반환suspend()
는 쓰레드를 멈추게 한다. 이때 resume()
을 호출해야 다시 실행 대기 상태가 된다. stop()
은 호출되는 즉시 쓰레드가 종료된다.
단, 위의 메서드는 deadlock를 일으키기 쉽기 때문에 사용이 권장되지 않는다.
자신이 하던 작업을 멈추고, 다른 쓰레드가 지정된 시간동안 작업을 수행하도록 한다.
Thread t = new Tread();
try {
t.join();
} catch(InterruptedException e) {...}
join()
은 특정 쓰레드에 대해 동작하므로 static 메서드가 아니다.
자신에게 주어진 실행시간을 다음 차례의 쓰레드에게 양보한다.
한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하게 막는 것을 말한다.
지정된 영역의 코드를 한 번에 하나의 쓰레드가 수행하는 것을 보장하는 방법이다.
synchronized
를 붙인다.{}
으로 감싸고 synchronized(참조변수)
를 붙인다.동기화된 임계 영역의 코드를 수행하다가 더 이상 진행할 상황이 아니면, 일단 wait()
을 호출하여 쓰레드가 락을 반납하고 기다리게 한다(waiting pool에서 기다림).
그러면 다른 쓰레드가 락을 얻어 해당 객체에 대한 작업을 수행할 수 있게 된다.
나중에 작업을 진행할 수 있는 상황이 되면 notify()
를 호출해서 작업을 중단했던 쓰레드가 다시 락을 얻어 작업을 진행할 수 있게 한다.
🔺notify()
는 waiting pool에서 대기 중인 쓰레드 중에서 하나를 임의로 선택해서 통지한다. 즉, 특정 조건을 만족하는 스레드만 깨울 수 없다. 이러한 문제는 condition
을 이용하면 된다. ReentrantLock class
와 같이 사용된다.