백엔드/Java의 정석

TIL 정리_25

ran4 2022. 3. 12. 23:33

쓰레드의 동기화

멀티쓰레드 프로세스에서는 다른 쓰레드의 작업에 영향을 미칠 수 있다

-> 한 쓰레드가 진행중인 작업을 다른 쓰레드가 간섭하지 못하도록 막는 것이다

 

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&notify대신 await()signal()을 사용한다

wait()&notify()와 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 블록을 사용해도 같은 효과를 얻을 수 있다

*상수에는 붙일 수 없다

 

volatilelongdouble을 원자화

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

 

람다식은 익명 함수가 아니라 익명 객체이다

->람다식을 다루기 위한 참조변수가 필요하다 

'백엔드 > Java의 정석' 카테고리의 다른 글

TIL 정리_27  (0) 2022.03.14
TIL 정리_26  (0) 2022.03.13
TIL 정리_24  (0) 2022.03.11
TIL 정리_23  (0) 2022.03.10
TIL 정리_22  (0) 2022.03.09