쓰레드(1)

김형준 Kim Hyeong Jun·2022년 12월 12일
0

멀티 쓰레드

장점

  1. cpu 사용률을 향상시킨다.
  2. 자원을 보다 효율적으로 사용할 수 있다.
  3. 사용자에 대한 응답성이 향상된다.
  4. 작업이 분리되어 코드가 간결해진다.

단점(주의 할 점)

  1. 동기화(synchronization) 이슈
  2. 교착상태(deadlock)

동기화 이슈

작업들 사이에 실행 시기를 맞춰주는 것을 동기화라 한다.
여러 쓰레드가 동일한 자원에 접근할 때 동기화 이슈가 발생하는데,
동일한 자원을 여러 쓰레드가 동시에 수정하려할 때
각각 쓰레드 결과에 영향을 주는데 이를 동기화 이슈라고 한다.

교착상태

둘 이상의 쓰레드가 서로의 작업이 끝나기만을 기다리며 작업을 더 이상 진행하지 못하는 상태를 의미한다.
교착상태는 상호 배제, 비선점, 점유와 대기, 원형 대기,
이렇게 4가지 조건이 모두 충족되어야지 발생한다.

상호 배제

  • 한 쓰레드가 접근하는 공유자원은 다른 쓰레드가 접근하지 못하도록 막아야 한다. 해당 임계영역을 하나의 쓰레드만접근할 수 있도록 동기화를 해주어야하며 다른 쓰레드가 동시에 사용할 수 없기에 교착 상태가 발생할 수 있다.

비선점

  • 한 쓰레드가 사용중인 동기화 객체(자원)는 중간에 다른 쓰레드가 빼앗을 수 있는 선점자원이 아니다. 해당 자원을 빼앗을 수 없다면 공유할수도 없기에 교착 상태가 발생할 수 있다.

점유와 대기

  • 쓰레드가 어떤 동기화 객체(자원)를 할당받은 상태에서 다른 동기화 객체(자원)를 기다리는 상태여야 한다.
  • 다른 쓰레드의 작업 진행을 방해하는 교착 상태가 발생하려면 다른 쓰레드가 필요로 하는 자원을 점유하고 있으면서 또 다른 자원을 기다리는 상태가 되어야 한다.

원형 대기

  • 점유와 대기를 하는 쓰레드간의 관계가 원을 이루어야 한다.
  • 쓰레드가 특정 자원에 대해 점유와 대기를 한다고 해서 모두 교착상태에 빠지는 것은 아니고 점유와 대기를 하는 쓰레드들이 서로 방해하는 방향이 원을 이루면 서로 양보하지 않기에 교착 상태에 빠진다.

구현과 실행

Thread클래스를 상속받는 방법과 Runnuble인터페이스를 구현하는 방법, 이렇게 두 가지가 있다.
상속을 받는 방법은 다른 클래스 상속을 못받기 때문에,
인터페이스를 구현하는 방법이 일반적으로 사용된다.
추가적으로 재사용성이 높고 코드의 일관성을 유지할 수 있기에 보다 객체지향적인 방법이다.

구현

class MyThread extends Thread {
	@Overriding
	public void run() { /* 작업 내용 */ } // Thread 클래스의 run()을 오버라이딩
}
class MyRunnableThread implements Runnable {
	public void run() { /* 작업 내용 */ } // Runnable 인터페이스의 run()을 구현
}

실행

public static void main (String args[]) {
	MyThread t1 = new Mytread();
    
    MyRunnableThread r = new MyRunnableThread();
    Thread t2 = new Thread(r);
    
    t1.start();
    t2.start();
} 

주의

한 번 실행이 종료된 쓰레드는 다시 실행할 수 없다.
즉, 하나의 쓰레드에 대해 start()는 한 번만 호출될 수 있다.
만약 쓰레드의 작업을 한 번 더 수행해야 한다면 새로운 쓰레드를 생성한 다음에 start()를 호출해야 한다.

만약

MyRunnableThread r = new MyRunnableThread();
Thread t2 = new Thread(r);

t2.start();

t2.start();

위와 같이 하나의 쓰레드에 대해 start()를 두 번 이상 호출하게 되면 IllegalThreadStateException이 발생한다.

start()와 run()

run()

main 메서드에서 run()을 호출하는 것은 생성된 쓰레드를 실행시키는 것이 아니라
단순히 클래스에 선언된 메서드를 호출하는 것 뿐이다.

start()

반면 start()는 새로운 쓰레드가 작업을 실행하는데 필요한 호출스택을 생성한 다음에 run()을 호출해서,
생성된 호출 스택에 run()이 첫 번째로 올라게 한다.
모든 쓰레드는 독립적인 작업을 수행하기 위해 자신만의 호출스택을 필요로 하기 때문이다.

Main 쓰레드

main 메서드의 작업을 수행하는 것도 쓰레드이다.
우리는 이를 main 쓰레드라고 한다.

실행 중인 사용자 쓰레드가 하나도 없을 때 프로그램은 종료된다.

쓰레드의 종류에는

  1. 사용자 쓰레드 (또는 논데몬 쓰레드)
  2. 데몬 쓰레드

가 있다.

싱글쓰레드와 멀티쓰레드

위의 그림처럼

  • 하나의 쓰레드로 두 작업을 처리하는 경우는 한 작업을 마친 후에 다른 작업을 시작하지만,
  • 두 개의 쓰레드로 작업 하는 경우에는 짧은 시간동안 2개의 쓰레드가 번갈아 가면서 작업을 수행해서 동시에 두 작업이 처리되는 것 같이 느끼게 한다.

두 작업이 모두 완료되는데 걸리는 시간은 오히려 멀티 쓰레드 쪽이 길다.
그 이유는 쓰레드간의 작업 전환(context switching)에 어느정도의 시간이 걸리기 때문이다.

profile
I want be a developer🙂

0개의 댓글