[JAVA]쓰레드 (2)

yoon·2023년 12월 11일
0

java

목록 보기
13/19
post-thumbnail

✅ 쓰레드

✔ I/O blocking

두 쓰레드가 서로 다른 자원을 사용하는 작업의 경우에는 싱글 쓰레드 프로세스보다 멀티쓰레드 프로세스가 더 효율적이다.

예를 들어 사용자의 입력이 필요한 경우 싱글 쓰레드라면 입력을 기다리는 동안 다른 작업을 할 수 없다. 그러나 멀티쓰레드라면 입력을 기다리는 동안 다른 쓰레드가 작업을 할 수 있다.

쓰레드가 입출력(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를 호출해야한다.

✔ start(), run()

  • run : 생성된 쓰레드를실행시키는 것이 아니라 단순히 클래스에 선언된 메서드를 호출하는 것이다.
  • start : 새로운 쓰레드가 작업을 실행하는 데 필요한 all stack을 생성한 다음, run을 호출한다. 쓰레드는 독립적인 작업을 수행하기 때문에 자신만의 call stack이 필요하다.

✔ 우선 순위 (priority)

쓰레드는 우선 순위(priority)라는 속성을 가지고 있다.
이 우선 순위의 값에 따라 쓰레드가 얻는 실행 시간이 달라진다.
우선 순위 범위는 1-10이며 숫자가 높을 수록 우선 순위가 높다.

메인 쓰레드의 디폴트 값은 5이다.

Thread t1 = new Thread();
t1.setPriority(7);
t1.getPriority();

단, 자바는 쓰레드가 우선 순위에 따라 처리되는 로직이 강제되지 않기 때문에 JVM마다 실행 결과가 다를 수 있다.

✔ 쓰레드 그룹 (thread group)

자신이 속한 쓸드 그룹이나 하위 그룹은 변경할 수 있지만 다른 쓰레드 그룹의 쓰레드는 변경하지 못한다.

Thread(TreadGroup group, String name)

우리가 생성하는 모든 쓰레드 그룹은 main쓰레드 그룹의 하위 쓰레드 그룹이 되며, 그룹을 지정하지 않고 생성한 쓰레드는 자동적으로 main쓰레드에 속한다.

✔ 데몬(daemon) 쓰레드

Tread t = new Thread();
t.setDaemon(true); // 반드시 start() 전에 실행되어야 함
t.start();
  • 데몬 쓰레드
    • 일반 쓰레드의 작업을 돕는 보조적인 역할을 수행하는 쓰레드이다.
    • 일반 쓰레드가 모두 종료되면 데몬 쓰레드는 강제적으로 자동 종료된다.
    • 데몬 쓰레드가 생성한 쓰레드는 자동적으로 데몬 쓰레드가 된다.

✔ 쓰레드의 상태

🔎 상태 설명

  • new : 쓰레드가 생성되고 start()가 호출되지 않은 상태
  • runnalble : 실행 중 or 실행 가능한 상태
  • blocked : lock이 풀릴 때 까지 기다리는 상태 > 동기화 블럭에 의해 발생
  • wating : unrunnable 한 일시 정지 상태
  • terminated :쓰레드 작업이 종료된 상태
  1. 쓰레드가 생성되고 start()가 호출되면 우선 실행대기열(Queue)에 저장되어 자신의 차례를 기다린다.
  2. 자신의 차례가 되면 실행 상태가 된다.
  3. 주어진 실행 시간이 다 되거나 yield()를 만나면 다시 실행 대기 상태가 되고, 다음 차례의 쓰레드가 실행된다.
  4. 실행이 모두 마치거나 stop()이 호출되면 쓰레드는 소멸된다.

✔ 쓰레드의 스케줄링 메서드

◾ sleep() : 지정된 시간동안 쓰레드 멈춤

try {
	Thread.sleep(1000); // 1초 멈춤
	} catch(InterruptedException e) {...}

sleep()에 의해 일시정지 된 쓰레드는 interrupt()가 호출되면 InterruptedException이 발생하여 잠에서 깨어나 실행 대기 상태가 된다.
그래서 항상 try-catch문으로 예외 처리를 해줘야한다.

◾ interrupt() : 작업 취소

진행 중인 쓰레드의 작업이 끝나기 전에 취소 시켜야할 때 사용한다.
단지 멈추라고 요청하는 것일 뿐 쓰레드를 강제 종료시키지는 못한다.

Thread t = new Thread();
t.start();
...
t.interrupt();
  • interrupted(): 현재 쓰레드의 interrupted 상태를 반환 후 , false로 변경
  • isInterrupted():쓰레드의 interrupted 상태를 반환

◾ suspend(), resume(), stop()

suspend()는 쓰레드를 멈추게 한다. 이때 resume()을 호출해야 다시 실행 대기 상태가 된다. stop()은 호출되는 즉시 쓰레드가 종료된다.
단, 위의 메서드는 deadlock를 일으키기 쉽기 때문에 사용이 권장되지 않는다.

◾ join() : 다른 쓰레드의 작업 기다림

자신이 하던 작업을 멈추고, 다른 쓰레드가 지정된 시간동안 작업을 수행하도록 한다.

Thread t = new Tread();
try {
	t.join();
	} catch(InterruptedException e) {...}

join()은 특정 쓰레드에 대해 동작하므로 static 메서드가 아니다.

◾ yield() : 다른 쓰레드에게 양보

자신에게 주어진 실행시간을 다음 차례의 쓰레드에게 양보한다.

✔ 쓰레드 동기화 (synchronization)

한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하게 막는 것을 말한다.

◾ synchronized

지정된 영역의 코드를 한 번에 하나의 쓰레드가 수행하는 것을 보장하는 방법이다.

  1. 메서드 앞에 synchronized를 붙인다.
    이때 메서드 전체가 임계 영역으로 설정된다.
    쓰레드는 synchronized메서드가 호출된 시점부터 해당 메서드가 포함된 객체의 lock을 얻어 작업을 수행하다가 메서드가 종료되면 lock을 반환한다.

  2. 메서드 내의 코드 일부를 {}으로 감싸고 synchronized(참조변수)를 붙인다.
    참조 변수는 lock을 걸고자하는 객체를 참조하는 것이어야한다. 이 블럭의 영역 안으로 들어가면서부터 쓰레드는 지정된 객체의 lock을 얻게 되고, 이 블럭을 벗어나면 lock을 반납한다.

◾ wait(), notify()

동기화된 임계 영역의 코드를 수행하다가 더 이상 진행할 상황이 아니면, 일단 wait()을 호출하여 쓰레드가 락을 반납하고 기다리게 한다(waiting pool에서 기다림).
그러면 다른 쓰레드가 락을 얻어 해당 객체에 대한 작업을 수행할 수 있게 된다.
나중에 작업을 진행할 수 있는 상황이 되면 notify()를 호출해서 작업을 중단했던 쓰레드가 다시 락을 얻어 작업을 진행할 수 있게 한다.

🔺notify()는 waiting pool에서 대기 중인 쓰레드 중에서 하나를 임의로 선택해서 통지한다. 즉, 특정 조건을 만족하는 스레드만 깨울 수 없다. 이러한 문제는 condition을 이용하면 된다. ReentrantLock class와 같이 사용된다.

profile
하루하루 차근차근🌱

0개의 댓글