티스토리 뷰
1. Thread Class 와 Executor 인터페이스 차이점은 ...?
Executor 인터페이스는 Java 4부터 도입된 동시성 프레임워크로서, 메모리 측면에서의 효율성있는 동시성을 지원하기 위한 인터페이스이다.
스레드를 생성하는 과정에서 메모리의 자원을 많이 소비하게 되는데, Executor 인터페이스는 스레드를 미리 생성하고, 그 수를 제한하며, 사용된 스레드를
재사용함으로써 자원을 효율성을 높여주고 있다.
Executor 인터페이스는 테스크를 정의한 객체를 생성하고, Executor 인터페이스에 태스크 객체를 제공하는 방식으로 동작한다. 테스크 객체를 제공하면
작업큐에 테스트가 저장되고, Executor에 미리 생성된 스레드는 작업큐에 있는 작업을 순차적으로 처리한다. 그리고 작업이 완료되면 스레드를 재생성하지 않고 해당 스레드에서 작업큐에 있는 다른 작업을 처리한다. Executor 인터페이스는 다음과 같은 특징을 가지고있다.
- 스레드 풀 사용 (FixedThreadPool : 스레드의 갯수 사용자가 임의로 고정, CachedThreadPool : 태스크의 숫자에 맞추어 스레드 갯수 유동적 변화)
- 태스크와 스레드 생성 작업을 분리하여 관리
- 스레드 풀안의 작업큐에서 작업을 꺼내서 스레드가 순차적으로 처리
- Executor Service를 더이상 필요 없으면 중지
- Executor Service가 멈추면 모든 스레드 중지
Executor 인터페이스에 작업을 할당할 때, 스레드가 해당 작업을 바로 수행하는 것이 아니다. 처음에는 바로 실행한다고 이해했지만, 앞서 언급한 것과 같이
즉시 수행이 아닌, 작업큐에 작업을 할당하는 것이다. 그리고 작업을 생성할 때, Runnable 과 Callable 두가지 방식이 있다. 두 방식 모두 동일하지만, 차이점은
단지, Executor 인터페이스가 작업을 완료하고, 결과값을 반환하는지의 유무이다. Callable은 작업 완료후, 작업의 결과를 Future 객체에 담아서 리턴한다.
2. 동시성이 있는 코드는 어떻게 테스트할 수 있는가 ...?
Junit의 @Test 어노테이션이 붙은 메소드는 코드가 종료되면, 다른 스레드의 동작 유무와 관계없이 종료되는 특성을 가지고 있어 테스트를 수행하는 것에 많은
어려움이 존재한다. 결국은 이 질문은 멀티스레드에서 동시성을 어떻게 제어할 수 있는가? 에 대한 질문이다. 멀티 스레드를 제어하는 방법 3가지를 보자!
- Executor 인터페이스
- 카운트다운래치(Count Down Latch)
: 큰 작업을 작은 작업으로 분리하고, 분리한 작업을 완료한 후에 흐름을 제어하는 클래스
: 카운트다운래치는 스레드의 흐름을 제어할 수 있는 join() 메소드의 단점을 보완하는데 효과적이다. join() 메소드를 호출한 메소드가 끝날 때까지 해당
다른 스레드가 실행되지 않도록 상태를 제어할 수 있다. 그러나 여러 스레드에 대해서는 처리하기가 힘들다. 이처럼 여러 개의 스레드에 대해 흐름을
제어할 때, 카운트다운래치가 효과적이다. 카운트다운래치는 생성할 때 1이상의 값을 인자값으로 받고, 대기상태가 된다. 그리고 카운트래치를 생성할 때
설정한 특정 방식을 수행할 때마다 카운트래치의 카운트값을 감소시키고, 0이 되면 대기상태가 해제되는 방식으로 동작한다.
→ 카운트다운래치는 여러개의 스레드의 흐름제어에 효과적이다.
- 포크조인프레임워크 : 작업을 분리하고(fork), 실행한 결과를 병합(Join)하는방식
작업이 분리되지 않는 경우 : Executor 인터페이스
작업이 분리되는 경우 : 카운트다운래치, 포크조인 (특히, 대량의 데이터의 정렬, 최소, 최대, 합산하는 경우, 네트워크 전체 스캔 하는 경우)
3. 멀티스레드 동기화
프로세스는 각각 자원을 보유하고 있기 때문에 서로 독립적이지만, 스레드는 힙, 런타임 컨텍스트 풀 영역, 메소드 영역을 공유하고 있기 때문에 공유 자원으로
인해 동기화 문제 발생 가능성이 존재한다. 결국 동기화는, 공유 자원에 대한 접근을 제어하기 위한 메커니즘이라고 정의할 수 있다.
임계영역이란, 한가지 자원에 대해 하나의 스레드만 접근 가능한 영역을 의미한다. 멀티스레드에서 임계영역을 처리하기 위해 Lock, Monitor, Synchronize 가 필요하다. Lock은 여러 스레드가 동시에 접근하지 못하도록 객체가 힙 영역에 생성될 때, 자동으로 생성된다. Synchronized 키워드가 붙은 메소드 또는 블록을
사용하면 Monitor가 객체의 Lock을 검사하고, Lock 현재 유무를 확인하여 객체를 보호한다. 기본 동작원리는 아래와 같다.
- 스레드가 Synchronized 키워드를 사용한 메소드, 블록에 접근하면, Monitor는 해당 객체의 접근 유무를 검사
- Lock이 다른 스레드에 의해 사용하고 있지 않다면, JVM에게 메세지 전달
- JVM은 Monitorenter 라는 내부명령으로 해당 객체의 Lock을 요청한 스레드에게 전달
만약, Lock이 사용중이라면, Lock을 반환할때까지 요청한 스레드는 대기
- 스레드는 Synchronized 키워드가 붙은 메소드, 블록에 대한 접근이 끝나면, Monitorexit 실행해서 Lock을 해당 객체에게 반환
스레드는 wait, norify, notifyAll 메소드를 통해 스레드의 상태를 이용해서 제어가 가능하다.
4. Atomic 클래스는 무엇을 제공하는가?
멀티 스레드에서 각 자원의 공유 때문에 동기화 문제가 많이 발생하고 있으며, 그중 힙 영역 내부에 있는 필드에 대한 동시성을 해치는 경우가 가장 빈번하게
발생한다. 이런 필드의 동기화를 제어할 수 있는 기능이 Atomic 클래스이다. 원자성은 데이터베이스에서 각각의 데이터의 일관성을 보장하기 위한 중요한
성질이다. Atomic 클래스를 통해 각 필드에 대한 동시성 보장이 가능하다.
5. 스레드와 프로세스의 차이
프로세스와 스레드의 차이점은 독립적인 자원을 가지고 있는지에 대한 유무이다. 프로세스는 CPU로부터 시스템 자원을 각각 할당받아 독립적으로 실행중인
프로그램을 의미한다. CPU로부터 각각 독립된 Code, Data, Stack, Heap 영역을 할당받는다. 각각의 프로세스는 독립적인 메모리 영역을 가지고 있기 때문에
다른 프로세스의 자원에 접근하기 위해서는 IPC (Inter Process Communication)를 사용해야 한다. (메일슬롯, 파이프, 파일, 소켓 등을 이용하여 통신)
스레드는 프로세스 내부의 작업 단위를 의미하며, 프로세스 내부에 있는 일부 자원을 스레드끼리 공유한다. 대표적인 힙 영역을 서로 공유하면서 사용함으로써
시블링 스레드가 변경한 자원을 특정 통신없이 바로 확인할 수 있다. 그러나 스택 영역은 서로 공유하지 않는다. 스택 영역을 공유하지 않는다는 것은 각각의
스레드에서 메소드를 독립적으로 실행할 수 있다는 것이다. 최소한의 자원을 공유함으로써 각각의 작업의 동시성을 제공을 하는 것이 스레드이다.
'면접 질문' 카테고리의 다른 글
[면접 준비] AOP (0) | 2019.03.08 |
---|---|
[면접준비] TDD, Cookie & Session, OneToMany, public static void main() (0) | 2019.02.08 |
[면접 준비] Garbage Collection (0) | 2019.02.01 |
자바에서 자주 질문되는 내용 정리 - 2 (1) | 2018.11.11 |
자바에서 자주 질문되는 내용 정리 - 1 (0) | 2018.11.10 |