쓰레드의 동기화
멀티쓰레드 프로세스에서는 다른 쓰레드의 작업에 영향을 미칠 수 있다
-> 한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것이다
synchronized를 이용한 동기화
1. 메서드 전체를 임계 영역으로 지정 public synchronized void calcSum() { //임계영역(critical section) } -> 한번에 1쓰레드를 사용, 최소화하기 2. 특정한 영역을 임계 영역으로 지정 synchronized(객체의 참조변수) { //임계영역(critical section) } |
wait()와 notify()
동기화된 임계 영역의 코드를 수행하다가 작업을 더 이상 진행할 상황이 아니면
wait()를 호출하여 쓰레드가 락을 반납하고 기다리게 한다, 나중에 진행할 수 있는 상황이 되면 notify()를 호출한다
=> 동기화의 효율을 높이기 위해 wait() notify()를 사용
Object클래스에 정의되어 있으며, 동기화 블록 내에서만 사용할 수 있다
wait(), notify(), notifyAll()
wait() : 객체의 lock을 풀고 쓰레드를 해당 객체의 waiting pool에 넣는다
notify() : waiting pool에서 대기중인 쓰레드 중의 하나를 깨운다
notyfyAll() : waiting pool에서 대기중인 모든 쓰레드를 깨운다 //일반적으로 사용한다
**기아현상과 경쟁상태
한 쓰레드가 계속 통지를 받지 못하고 오랫동안 기다리는 것을 기아(starvation)현상이라고 한다
이 현상을 막기위해 notify()가 아닌 notifyAll()을 사용한다
Lock과 Condition을 이용한 동기화
java.util.concurrent.locks 패키지가 제공하는 lock 클래스이다 //JDK 1.5추가
ReentrantLock : 재진입이 가능한 lock, 가장 일반적인 배타 lock이다 ReentrantReadWriteLock : 읽기에는 공유적이고 쓰기에는 배타적인 lock StampedLock : ReentrantReadWriteLock에 낙관적인 lock의 기능을 추가 //1.8 |
**무조건 읽기 lock을 걸지 않고, 쓰기와 읽기가 충돌할 때만 쓰기가 끝난 후에 읽기 lock을 거는것이다
ReentrantLock의 생성자
ReentrantLock()
ReentrantLock(boolean fair) //공정하게 처리 성능은 떨어짐
synchronized : 자동으로 잠금과 해제가 관리된다
ReentrantLock : 수동으로 lock을 잠그고 해제한다
void lock() lock을 잠근다
void unlock() lock을 해지한다 //일반적으로 try-catch문으로 감싼다
boolean isLocked() lock이 잠겼는지 확인한다
tryLock()메서드
다른 쓰레드에 의해 lock이 걸려있으면 기다리지 않거나 지정된 시간만큼만 기다린다
boolean tryLock()
boolean tryLock(long timeout, TimeUnit unit,) throws InterruptedExceiption
ReentrantLock과 Condition
wait¬ify대신 await()과 signal()을 사용한다
wait()¬ify()와 await()&signal()의 비교
Object | Condition |
void wait() | void await() void awaitUninterruptibly() |
void wait(long timeout) | boolean await(long time, TimeUnit unit) long awaitNanos(long nanosTimeout) boolean awaitUntil(Date deadline) |
void notify() | void signal() |
void notifyAll() | void sinalAll() |
volatile
변수 앞에 volatile을 붙이면 코어가 변수의 값을 읽어올 때 캐시가 아닌 메모리에서
읽어오기 때문에 캐시와 메모리간의 값의 불일치가 해결된다
synchronized 블록을 사용해도 같은 효과를 얻을 수 있다
*상수에는 붙일 수 없다
volatile로 long과 double을 원자화
volatile long sharedVal;
volatile double sharedVal;
fork & join 프레임 워크 //1.7 추가
RecursiveAction 반환값이 없는 작업을 구현할 때 사용
RecursiveTask 반환값이 있는 작업을 구현할 때 사용
fork() : 해당 작업을 쓰레드 풀의 작업 큐에 넣는다. //비동기 메서드
join() : 해당 작업의 수행이 끝날 때까지 기다렸다가 수행이 끝나면 그 결과를 반환한다 //동기 메서드
람다식 //JDK 1.8
메서드를 하나의 식으로 표현한 함수형 언어이다
메서드를 람다식으로 표현하면 이름과 반환값이 없어지므로 람다식을 '익명함수'라고도 한다
int[] arr = new int[5];
Arrays.setAll(arr, (i) -> (int)(Math.random()*5)+1); //굵은 글씨가 람다식이다
**함수와 메서드의 차이
근본적으로는 동일하다. 함수는 일반적 용어, 메서드는 객체지향개념 용어이다
함수는 클래스에 독립적, 메서드는 클래스에 종속적이다
람다식 작성
메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통{ } 사이에 '->'을 추가한다
int max(int a, int b) { return a>b ? a:b; } ↓ |
(int a, int b) { return a>b ? a:b; } ↓ |
반환값이 있는 메서드의 경우 return문 대식 식을 사용 (int a, int b) -> a>b ? a:b ↓ |
매개변수의 타입이 추론 가능할때 생략가능 (a, b) -> a>b a:b |
람다식 작성 시 주의사항
-매개변수가 하나인 경우 괄호() 생략가능(타입이 없을때만)
-블록 안의 문장이 하나뿐 일 때, 괄호{} 생략가능 //문장이 아닌 식이기 때문에 끝에 ; 안붙임)
-하나뿐인 문장이 return문이면 괄호{} 생략불가 //대부분 리턴을 생략해서 상관x
람다식은 익명 함수가 아니라 익명 객체이다
->람다식을 다루기 위한 참조변수가 필요하다