Kotlin 스 레 드 동기 화 실현 방법
9712 단어 Kotlin스 레 드 동기 화
이 어"현재 Task 1,Task 2 등 여러 병행 작업 을 수행 하고 있 는데,어떻게 모든 수행 이 완료 되면 Task 3 를 수행 할 수 있 을 까"라 고 덧 붙 였 다.
Kotlin 에서 우 리 는 여러 가지 실현 방식 이 있 습 니 다.본 고 는 이 모든 방식 을 정리 하고 소장 하 는 것 을 권장 합 니 다.
1. Thread.join
2. Synchronized
3. ReentrantLock
4. BlockingQueue
5. CountDownLatch
6. CyclicBarrier
7. CAS
8. Future
9. CompletableFuture
10. Rxjava
11. Coroutine
12. Flow
우 리 는 먼저 세 개의 Task 를 정의 하고 상기 장면 을 모 의 합 니 다.Task 3 는 Task 1,Task 2 를 바탕 으로 돌아 오 는 결과 연결 문자열 을 기반 으로 합 니 다.모든 Task 는 sleep 를 통 해 모 의 시간 을 소모 합 니 다.
val task1: () -> String = {
sleep(2000)
"Hello".also { println("task1 finished: $it") }
}
val task2: () -> String = {
sleep(2000)
"World".also { println("task2 finished: $it") }
}
val task3: (String, String) -> String = { p1, p2 ->
sleep(2000)
"$p1 $p2".also { println("task3 finished: $it") }
}
1. Thread.join()Kotlin 호 환 자바,자바 의 모든 스 레 드 도 구 는 기본적으로 사용 할 수 있 습 니 다.그 중에서 가장 간단 한 스 레 드 동기 화 방식 은 Thread 의 join()을 사용 하 는 것 입 니 다.
@Test
fun test_join() {
lateinit var s1: String
lateinit var s2: String
val t1 = Thread { s1 = task1() }
val t2 = Thread { s2 = task2() }
t1.start()
t2.start()
t1.join()
t2.join()
task3(s1, s2)
}
2. Synchronizedsynchronized 자 물 쇠 를 사용 하여 동기 화
@Test
fun test_synchrnoized() {
lateinit var s1: String
lateinit var s2: String
Thread {
synchronized(Unit) {
s1 = task1()
}
}.start()
s2 = task2()
synchronized(Unit) {
task3(s1, s2)
}
}
그러나 세 개의 작업 이 넘 으 면 synchrnoized 라 는 쓰기 가 어색 합 니 다.여러 병렬 작업 의 결 과 를 동기 화하 기 위해 서 는 n 개의 자 물 쇠 를 설명 하고 n 개의 synchronized 를 끼 워 넣 어야 합 니 다.3. ReentrantLock
ReentrantLock 은 JUC 가 제공 하 는 스 레 드 잠 금 으로 synchronized 를 교체 하여 사용 할 수 있 습 니 다.
@Test
fun test_ReentrantLock() {
lateinit var s1: String
lateinit var s2: String
val lock = ReentrantLock()
Thread {
lock.lock()
s1 = task1()
lock.unlock()
}.start()
s2 = task2()
lock.lock()
task3(s1, s2)
lock.unlock()
}
ReentrantLock 의 장점 은 여러 개의 병렬 작업 이 있 을 때 synchrnoized 가 내장 되 어 있 는 문제 가 발생 하지 않 지만 여러 개의 lock 관리 가 다른 작업 을 만들어 야 한 다 는 것 입 니 다.4. BlockingQueue
차단 대기 열 내부 도 Lock 을 통 해 이 루어 지기 때문에 동기 화 잠 금 효과 도 얻 을 수 있 습 니 다.
@Test
fun test_blockingQueue() {
lateinit var s1: String
lateinit var s2: String
val queue = SynchronousQueue<Unit>()
Thread {
s1 = task1()
queue.put(Unit)
}.start()
s2 = task2()
queue.take()
task3(s1, s2)
}
물론 차단 대기 열 은 생산/소비 장면 에서 의 동기 화 에 더 많이 사용 된다.5. CountDownLatch
JUC 의 자 물 쇠 는 대부분 AQS 를 기반 으로 이 루어 지 며 독점 자물쇠 와 공유 자물쇠 로 나 눌 수 있다.ReentrantLock 은 일종 의 독점 자물쇠 다.이에 비해 공유 자 물 쇠 는 이 장면 에 더욱 적합 하 다.예 를 들 어 Countdown Latch 는 다른 스 레 드 가 모두 실 행 될 때 까지 스 레 드 를 차단 할 수 있 습 니 다.
@Test
fun test_countdownlatch() {
lateinit var s1: String
lateinit var s2: String
val cd = CountDownLatch(2)
Thread() {
s1 = task1()
cd.countDown()
}.start()
Thread() {
s2 = task2()
cd.countDown()
}.start()
cd.await()
task3(s1, s2)
}
공유 자물쇠 의 장점 은 모든 작업 을 위해 별도의 자 물 쇠 를 만 들 필요 가 없 으 며,아무리 많은 병행 작업 을 해도 쉽게 쓸 수 있다 는 것 이다.6. CyclicBarrier
Cyclic Barrier 는 JUC 가 제공 하 는 또 다른 공유 잠 금 장치 로 한 스 레 드 가 동기 점 에 도착 한 후에 다시 함께 실행 할 수 있 습 니 다.그 중에서 임의의 스 레 드 가 동기 점 에 이 르 지 못 하면 다른 스 레 드 가 모두 막 힐 수 있 습 니 다.
CountDownlatch 와 차이 점 은 CountDownlatch 가 일회 성 이 며,Cyclic Barrier 는 리 셋 후 중복 사용 할 수 있 습 니 다.이것 이 바로 Cyclic 의 명명 유래 로 순환 적 으로 사용 할 수 있 습 니 다.
@Test
fun test_CyclicBarrier() {
lateinit var s1: String
lateinit var s2: String
val cb = CyclicBarrier(3)
Thread {
s1 = task1()
cb.await()
}.start()
Thread() {
s2 = task1()
cb.await()
}.start()
cb.await()
task3(s1, s2)
}
7. CASAQS 내 부 는 자 회전 자 물 쇠 를 통 해 동기 화 되 고 자 회전 자물쇠 의 본질은 CompareAndSwap 을 이용 하여 스 레 드 가 막 히 는 비용 을 피 하 는 것 이다.
따라서 우 리 는 CAS 에 기반 한 원자 계 수 를 사용 하여 잠 금 없 는 조작 을 실현 하 는 목적 을 달성 할 수 있다.
@Test
fun test_cas() {
lateinit var s1: String
lateinit var s2: String
val cas = AtomicInteger(2)
Thread {
s1 = task1()
cas.getAndDecrement()
}.start()
Thread {
s2 = task2()
cas.getAndDecrement()
}.start()
while (cas.get() != 0) {}
task3(s1, s2)
}
while 순환 공전 은 자원 을 낭비 하 는 것 처럼 보이 지만 자 회전 자물쇠 의 본질은 이 렇 기 때문에 CAS 는 일부 cpu 밀집 형의 짧 은 작업 동기 화 에 만 적용 된다.volatile
CAS 의 잠 금 없 는 실현 을 보면 많은 사람들 이 volatile 을 생각 할 것 이다.잠 금 없 는 스 레 드 안전 도 실현 할 수 있 을 까?
@Test
fun test_Volatile() {
lateinit var s1: String
lateinit var s2: String
Thread {
s1 = task1()
cnt--
}.start()
Thread {
s2 = task2()
cnt--
}.start()
while (cnt != 0) {
}
task3(s1, s2)
}
주의 하 세 요.이런 문법 은 잘못된 것 입 니 다.volatile 은 가시 성 을 보장 할 수 있 으 나 원자 성 을 보장 할 수 없습니다.cnct-스 레 드 안전 이 아니 므 로 잠 금 작업 이 필요 합 니 다.
8. Future
위 에 자물쇠 조작 이 있 든 없 든 두 변 수 를 정의 해 야 합 니 다.s1,s2 기록 결 과 는 매우 불편 합 니 다.
자바 1.5 를 시작 으로 Callable 과 Future 를 제공 하여 작업 수행 이 끝 날 때 결 과 를 되 돌 릴 수 있 습 니 다.
@Test
fun test_future() {
val future1 = FutureTask(Callable(task1))
val future2 = FutureTask(Callable(task2))
Executors.newCachedThreadPool().execute(future1)
Executors.newCachedThreadPool().execute(future2)
task3(future1.get(), future2.get())
}
future.get()을 통 해 결과 가 돌아 오 기 를 동시에 기다 릴 수 있어 쓰기 에 매우 편리 합 니 다.9. CompletableFuture
future.get()은 편리 하지만 스 레 드 를 막 을 수 있 습 니 다.Java 8 에 CompletableFuture 가 도입 되 었 습 니 다. ,퓨 처 인 터 페 이 스 를 구현 하면 서 컴 플 리 케 이 션 스테이지 인 터 페 이 스 를 구현 했다.Complete bleFuture 는 여러 개의 Complete Stage 를 논리 적 으로 조합 하여 복잡 한 비동기 프로 그래 밍 을 실현 할 수 있다.이러한 논리 적 조합 방법 은 리 셋 형식 으로 라인 의 막힘 을 피 했다.
@Test
fun test_CompletableFuture() {
CompletableFuture.supplyAsync(task1)
.thenCombine(CompletableFuture.supplyAsync(task2)) { p1, p2 ->
task3(p1, p2)
}.join()
}
10. RxJavaRxJava 가 제공 하 는 각종 연산 자 와 스 레 드 전환 능력 역시 우리 가 수 요 를 실현 하 는 데 도움 을 줄 수 있다.
zip 연산 자 는 두 개의 Observable 결 과 를 조합 할 수 있 습 니 다.subscribeOn 은 비동기 작업 을 시작 하 는 데 사 용 됩 니 다.
@Test
fun test_Rxjava() {
Observable.zip(
Observable.fromCallable(Callable(task1))
.subscribeOn(Schedulers.newThread()),
Observable.fromCallable(Callable(task2))
.subscribeOn(Schedulers.newThread()),
BiFunction(task3)
).test().awaitTerminalEvent()
}
11. Coroutine앞에서 그렇게 많이 말 했 는데 사실은 모두 자바 의 도구 이다.Coroutine 은 마침내 Kotlin 특유 의 도구 라 고 할 수 있 습 니 다.
@Test
fun test_coroutine() {
runBlocking {
val c1 = async(Dispatchers.IO) {
task1()
}
val c2 = async(Dispatchers.IO) {
task2()
}
task3(c1.await(), c2.await())
}
}
쓰기 에 매우 편 해서 앞의 각종 공구 의 장점 을 한 몸 에 모 았 다 고 할 수 있다.12. Flow
Flow 는 바로 Corotine 버 전의 RxJava 로 RxJava 의 연산 자 를 많이 가지 고 있 습 니 다.예 를 들 어 zip:
@Test
fun test_flow() {
val flow1 = flow<String> { emit(task1()) }
val flow2 = flow<String> { emit(task2()) }
runBlocking {
flow1.zip(flow2) { t1, t2 ->
task3(t1, t2)
}.flowOn(Dispatchers.IO)
.collect()
}
}
flow on 은 Task 를 비동기 로 계산 하고 결 과 를 발사 합 니 다.총결산
위의 이렇게 많은 방식 은 회향 두 의'희'자의 네 가지 표기 법 처럼 모두 파악 할 필요 가 없다.결론 적 으로 Kotlin 에서 가장 좋 은 스 레 드 동기 화 방안 의 첫 번 째 추진 과정!
여기 서 Kotlin 스 레 드 동기 화 에 관 한 몇 가지 실현 방법 에 관 한 글 은 여기까지 소개 되 었 습 니 다.더 많은 Kotlin 스 레 드 동기 화 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 저 희 를 많이 사랑 해 주세요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
머티리얼 디자인 도입 직후에 할 일안드로이드 프로젝트에 머티리얼 디자인을 도입한 후에 할 일을 적는다. Android 프로젝트를 만든 후 Hello world에 대해 수행합니다. 머티리얼 디자인을 도입하기 위해, build.gradle 를 이하와 같...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.