반응형

본 글은 책 자바의 정석을 ref 하여 요약한 것입니다.


 

프로세스

실행 중인 프로그램

하나의 프로세스는 하나 이상의 스레드 이상을 갖는다.

 

스레드

실제 작업을 수행하는 단위

 

멀티태스킹

여러 프로세스를 동시에 실행

 

멀티쓰레딩

여러 스레드가 동시에 실행

 

* 하나의 cpu코어는 번갈아서 여러 작업을 동시성 처리한다. 

매우 빠른 속도로 여러 스레드를 처리하기 때문에 병렬적으로 처리하는 것처럼 보인다.

 


 

구현

Thread를 상속받아서 run()을 오버 라이딩

Runnable을 구현해서 run()을 오버 라이딩, Thread 생성자의 인수로 제공

 

실행

start() 메서드로 시작, 사실 시작하는 것은 아니고 실행 대기상태가 된다.

* start()는 새로운 call stack을 생성한 후, run()을 호출한다.

* 한 스레드에서 예외가 발생해도 다른 스레드에 영향을 미치지 않는다.

종료된 스레드는 재실행이 불가능하다. 새로 생성하여 start() 해야 한다.

 


 

스레드 그룹

폴더 안에 폴더를 만들듯이 스레드 그룹에 하위 스레드 그룹을 생성할 수 있다.

쓰레드 그룹을 지정하지 않는다면 main 쓰레드 그룹에 속한다.

모든 스레드 그룹은 기본적으로 main스레드 그룹 하위에 속하게 된다.

 

데몬 쓰레드

일반 스레드가 모두 종료되면 강제적으로 종료되는 보조 쓰레드

데몬 스레드를 지정 후 start() 해야 한다. 역은 성립하지 않는다.

 


 

스레드의 실행 제어

NEW - 아직 start()되지 않은 상태

RUNNABLE - 실행 대기 상태, 실행 중

* 실행 대기열에서 저장되어 자신의 차례가 되면 실행

BLOCKED - 동기화 블록에 의해 일시 정지된 상태

WAITING, TIMED_WAITING - 일시 정지 상태

TERMINATED - 종료

 

 

static sleep()

스레드를 멈추게 한다.

항상 try - catch문으로 예외 처리해줘야 한다.

interrupt()가 호출되면 깨어나 실행 대기 상태가 된다.

Thread.sleep()   자신에게 적용한다. 

 

static yield()

다음 차례의 스레드에게 양보한다.

Thread.yield()   자신에게 적용한다.

 

join()

ThreadB.join()

A인 자신을 멈추고, ThreadB가 먼저 작업을 수행하도록 한다.

ThreadB가 작업을 마치면 수행한다.

항상 try - catch문으로 예외 처리해줘야 한다.

sleep()과 같이 interrupt()로 대기상태에서 벗어날 수 있다.

 

interrupt()

일종의 flag

1. 스레드를 멈추라고 요구한다.

** 강제로 멈추진 못하고 interrupted(), isInterrupted() 상태를 true로 바꾼다.

2. 일시정지 상태인 스레드를 실행 대기 상태로 바꾼다.

** 일시정지 상태에서 실행 대기 상태로 바뀌면, interrupted(), isInterrupted() 상태를 false로 바꾼다.

 


 

스레드의 동기화

공유 데이터를 사용하는 곳에 임계 영역을 설정하여 하나의 스레드만 접근할 수 있도록 한다.

1. 메서드 전체를 임계 영역으로 지정

2. 특정한 영역을 임계 영역으로 지정

** 공유 데이터가 있는 곳에 synchronized를 선언한다.

 

wait(), notify(), notifyAll()

** 동기화 블록 내에서만 사용할 수 있다.

** Object에 정의되어 있다.

 

번갈아 작업하는 교대작업 시

 

wait()

현재 공유 객체를 사용중인 쓰레드를 정지

스레드가 락을 반납하고 대기한다.

 

notify()

이전에 작업을 중지했던 임의의 스레드를 실행 대기 상태로 만든다.

오래 기다린 스레드가 락을 얻는 것을 보장하진 못한다.

 

** 제공자 스레드가 데이터를 충분히 제공했다면 wait(), 소모자 스레드를 깨운다. notify()

** 소모자 쓰레드가 데이터를 전부 소진했다면 wait(), 제공자 쓰레드를 깨운다. notify()

 

 

Lock과 Condition을 이용한 동기화

ReentrantLock 

** 락을 풀고 다시 락을 얻어서 진입한다.

** 수동으로 락을 걸고, 해제해야 한다.

 

ReentrantReadWriteLock

** 읽기 락이 걸려있다면 다른 읽기 락도 중복해서 접근할 수 있다.

 

StampedLock

** 낙관적 읽기 락이 추가됐다.

** 낙관적 읽기 락은 쓰기 락에 의해 바로 풀린다.

** 낙관적 읽기 락이 실패했다면, 읽기 락을 걸어서 읽어와야 한다.

 

 

Condition

스레드 종류에 따라 구분하여 통지할 수 있게 한다.

락으로부터 여러 Condition을 생성한다.

Condition을 제공자, 소모자에게 부여하여 통지 대상을 명확하게 한다.

 


 

volatile

싱글 코어에서는 문제가 없지만, 멀티 코어에서는 캐시 메모리에 저장된 값을 사용하여 문제가 발생한다.

volatile을 사용하게 되면 캐시가 아닌 메모리에서 읽어오기 때문에 불일치가 해결된다.

synchronized 또한 캐시와 메모리 간의 동기화가 이루어지기 때문에 불일치가 해결된다.

 

JVM이 4byte로 데이터를 처리하기 때문에 int 이하에서는 한 번에 읽고 쓰는 것이 가능하지만,

long, double은 8byte이기 때문에 데이터를 처리하는 중에 다른 스레드가 끼어들 수 있다.

이러한 문제를 해결하기 위해 변수에 volatile을 선언해주어 원자화로 만들어 주면 된다. (동기화는 아니다.)

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

+ Recent posts