책너두 (코딩 인터뷰 완전분석) 33일차 (~261p, 15.1 ~ 15.6)

15. 스레드와 락

  • 스레드로 알고리즘을 구현하라는 문제를 출제하는 일은 흔하지 않음.
  • 하지만 스레드, 특히 교착상태(deadlock)에 대한 일반적 이해도를 평가하기 위한문제느 어떤 회사에서도 상대적으로 자주 출제하는 편임.

자바의 스레드

  • 자바의 모든 스레드는 java.lang.Thread 클래스 객체에 의해 생성되고 제어됨.
  • 독립적인 응용 프로그램이 실행될 때, main() 메서드를 실행하기 위한 하나의 사용자 스레드(user thread)가 자동으로 만들어짐.
    • 이를 주 스레드(main thread) 라고 함.
  • 자바에서 스레드를 구현하는 방법은 2가지가 있음.
    • java.lang.Runnable 인터페이스 구현하기
    • java.lang.Thread 클래스 상속받기

Runnable 인터페이스를 구현하는 방법

public interface Runnable {
    void run();
}
  • 이 인터페이스를 사용해 스레드를 만들고 사용하려면 다음의 과정을 거쳐야 함.
    • Runnable 인터페이스를 구현하는 클래스를 만든다.
    • Thread 타입의 객체를 만들 때, Thread의 생성자에 Runnable 객체를 인자로 넘긴다.
    • 이전 단계에서 생성한 Thread 객체의 start() 메서드를 호출한다.

Thread 클래스 상속

  • Thread 클래스를 상속받아서 스레드를 만들 수도 있음.
    • 거의 항상 run() 메서드를 오버라이드해야 함.
    • 하위 클래스의 생성자는 하위 클래스의 생성자를 명시적으로 호출해야 함.

Thread 상속 vs. Runnable 인터페이스 구현

  • 스레드 생성할 때, Runnable 인터페이스를 구현하는 것이 Thread 상속받는 것보다 선호됨.
    • 자바는 다중 상속을 지원하지 않음. 따라서 Thread 클래스를 상속하게 도미ㅕㄴ 하위 클래스는 다른 클래스를 상속할 수 없음.
    • Thread 클래스의 모든 것을 상속받는 것이 너무 부담될 수 있음.

동기화와 락

  • 어떤 프로세스 안에서 생성된 스레드들은 같은 메모리 공간을 공유함.
    • 이게 좋을 때가 있고 나쁠 때가 있음.
  • 스레드가 서로 데이터를 공유할 수 있다는 점은 장점이긴 하지만, 두 스레드가 같은 자원을 동시에 변경하는 경우 문제가 됨.
  • 자바는 공유 자원에 대한 접근을 제어하기 위한 동기화 방법을 제공함.
  • synchronized와 Lock 이라는 키워드는 동기화 구현을 위한 기본이 됨.

동기화된 메서드

  • 통상적으로 synchronized 키워드를 사용할 떄는 공유 자원에 대한 접근을 제어한다.
    • 이 키워드는 메서드에 적용할 수도 있고, 특정한 코드 블록에 적용할 수도 있음.
    • 여러 스레드가 같은 객체를 동시에 실행하는 것 또한 방지해줌.
  • 정적 메서드(static method) 는 클래스 락(class lock)에 의해 동기화 됨.
    • 같은 클래스에 있는 동기화된 정적 메서드는 두 스레드에서 동시에 실행될 수 없음.

동기화된 블록

  • 특정한 코드 블록을 동기화할 수도 있음.
    • 메서드 동기화하는 것과 아주 비슷하게 동작함.

  • 좀 더 세밀하게 동기화를 제어하고 싶을 때는 락(lock)을 사용함.
  • 락(모니터(monitor)라고도 함)을 공유 자원에 붙이면 해당 자원에 대한 접근을 동기화할 수 있음.
    • 스레드가 해당 자원을 접근하려면 우선 그 자원에 붙어 있는 락을 획득(acquire)해야 함.
    • 특정 시점에 락을 쥐고 있을 수 있는 스레드는 하나뿐임.
    • 따라서, 해당 공유자원은 한 번에 한 스레드만이 사용할 수 있음.

교착상태와 교착상태 방지

  • 교착상태(deadlock)란 첫 번째 스레드는 두 번쨰 스레드가 들고 있는 객체의 락이 풀리기를 기다리고 있고, 두 번째 스레드 역시 첫 번쨰 스레드가 들고 있는 객체의 락이 풀리기를 기다리는 상황을 일컫는다.
    • 모든 스레드가 락이 풀리기를 기다리고 있기 떄문에 무한 대기 상태에 빠짐.
    • 이런 스레드를 교착 상태에 빠졌다고 함.
  • 교착 상태가 발생하려면 다음의 네 가지 조건이 모두 충족되어야 함.
    1. 상호 배제(mutual exclusion)
      • 한 번에 한 프로세스만 공유 자원을 사용할 수 있음.
      • 자원의 양이 제한되어 있더라도 교착 상태는 발생할 수 있음.
    2. 들고 기다리기(hold and wait)
      • 공유 자원에 대한 접근 권한을 갖고 있는 프로세스가, 그 접근 권한을 양보하지 않은 상태에서 다른 자원에 대한 접근 권한을 요구할 수 있음.
    3. 선취(preemption) 불가능
      • 한 프로세스가 다른 프로세스의 자원 접근 권한을 강제로 취소할 수 없음.
    4. 대기 상태의 사이클(circular wait)
      • 두 개 이상의 프로세스가 자원 접근을 기다리는데, 그 관계에 사이클(cycle)이 존재함.
  • 교착 상태를 방지하기 위해, 이 조건들 가운데 하나를 제거하면 됨.
    • 이 조건 가운데 상당수는 만족되기 어려운 것이여서 까다로움.
    • 공유 자원중 많은 경우, 한 번에 한 프로세스만 사용할 수 있기 떄문에, 1번 조건은 제거하기 어려움.
    • 대부분의 교착상태 방지 알고리즘은 4번 조건, 대기 상태의 사이클이 발생하는 일을 막는 데 초점이 맞춰져 있음.

면접 문제

15.1 프로세스 vs. 스레드

  • 프로세스와 스레드의 차이는 무엇인가?

15.2 문맥 전환

  • 문맥 전환(context switch)에 소요되는 시간을 측정하려면 어떻게 해야 할까?

15.3 철학자의 만찬

  • 유명한 철학자의 만찬 문제(dining philosophers problem)를 떠올려 보자. 철학자들은 원형 테이블에 앉아 있고 그들 사이에젓가락 한 짝이 놓여 있다. 음식을 먹으려면 젓가락 두 짝이 전부 필요한데, 철학자들은 언제나 오른쪽 젓가락을 집기 전에 왼쪽 젓가락을 먼저 집는다. 모든 철학자들이 왼쪽에 있는 젓가락을 동시에 집으려고 하면, 교착상태에 빠질 수 있다. 철학자들의 만찬 문제를 시뮬레이션하는 프로그램을 작성하라. 단, 스레드와 락을 사용하여 이 프로그램이 교착상태에 빠지지 않도록 하라.

15.4 교착상태 없는 클래스

  • 교착상태에 빠지지 않는 경우에만 락을 제공해주는 클래스를 설계해 보라.

15.5 순서대로 호출

  • 다음과 같은 코드가 있다고 하자.
public class Foo {
    public Foo() { ... }
    public void first() { ... }
    public void second() { ... }
    public void third() { ... }
}
  • Foo 인스턴스 하나를 서로 다른 세 스레드에 전달한다. threadA는 first를 호출할 것이고, threadB는 second를 호출할 것이며, threadC는 third를 호출할 것이다. first가 second보다 먼저 호출되고, second가 third보다 먼저 호출되도록 보장하는 메커니즘을 설계하라.

15.6 동기화된 메서드

  • 동기화된 메서드 A와 일반 메서드 B가 구현된 클래스가 있다. 같은 프로그램에서 실행되는 스레드가 두 개 존재할 때 A를 동시에 실행할 수 있는가? A와 B는 동시에 실행될 수 있는가?

댓글

Designed by JB FACTORY