Dispatcher.kt
package com.example.demo.process
import io.github.oshai.kotlinlogging.KotlinLogging
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit
import kotlin.concurrent.atomics.AtomicInt
import kotlin.concurrent.atomics.ExperimentalAtomicApi
import kotlin.concurrent.atomics.incrementAndFetch
object Dispatcher {
private val logger = KotlinLogging.logger {}
//获取当前机器的 CPU 核心数,后续所有线程池大小的计算都基于此
private val cpuCore = Runtime.getRuntime().availableProcessors()
//使用 AtomicInt 作为线程编号的原子计数器,保证线程命名的唯一性和线程安全。
@OptIn(ExperimentalAtomicApi::class)
private val newThreadId: AtomicInt = AtomicInt(0)
@OptIn(ExperimentalAtomicApi::class)
private fun createDispatcherThreadPool(name: String, minSize: Int, maxSize: Int): CoroutineDispatcher {
//核心线程数(下限 ≥1)
val corePoolSize = minSize.coerceAtLeast(1)
//最大线程数(范围:corePoolSize ~ 128)
val maximumPoolSize = maxSize.coerceIn(corePoolSize, 128)
logger.info { "create dispatcher $name, corePoolSize=$corePoolSize, maximumPoolSize=$maximumPoolSize" }
return ThreadPoolExecutor(
corePoolSize, //核心线程数,源码:workerCountOf(c) < corePoolSize就会创建线程,所以就算是是:任务一个执行完了来,
maximumPoolSize, // TODO 此参数其实压根不会生效,因为无界队列
30, //回收非核心线程。 源码中: workQueue.poll(30, SECONDS) ← 限时等待 30 秒, 如果线程没有任务,就会被回收(非核心线程),核心线程不会回收!!!
TimeUnit.SECONDS,
LinkedBlockingQueue(), // 无界队列
) { runnable ->
/*
线程命名 :格式为 {name}-{自增ID} ,例如 Scheduler-1 、 Actor-3 ,便于 jstack 排查。
且是守护线程
*/
Thread(runnable, "$name-${newThreadId.incrementAndFetch()}").apply { isDaemon = true }
}.asCoroutineDispatcher() //是 Kotlin 协程库( kotlinx.coroutines )提供的一个扩展函数,定义在 java.util.concurrent.Executor 上,用于把 Java 线程池(Executor) 桥接到 Kotlin 协程的调度器体系 中
}
// 轻量级调度器,固定 2 个核心线程,适合处理定时任务、延迟任务等轻量操作。
val Scheduler = createDispatcherThreadPool("Scheduler", 2, cpuCore / 2)
// 主力执行调度器,至少有 10 个核心线程,承载进程内大部分的业务逻辑执行
//Dispatcher.Actor.limitedParallelism(1)的意思是:复用这个Actor调度器的底层线程池,然后再创建一个子调度器,并发度是1,这样子
val Actor = createDispatcherThreadPool("Actor", cpuCore.coerceAtLeast(10), cpuCore * 2)
}
main.kt
package com.example.demo
import com.example.demo.process.Dispatcher
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.springframework.boot.autoconfigure.SpringBootApplication
import java.util.concurrent.TimeUnit
@SpringBootApplication
class DemoApplication
fun main(args: Array<String>) {
// runApplication<DemoApplication>(*args)
val actorScope = CoroutineScope(CoroutineName("DemoApplication") + Dispatcher.Actor)
val schedulerScope = CoroutineScope(Dispatcher.Scheduler)
//------------------------------------test1-------------------------------------
var count = 0
actorScope.launch(CoroutineName("TIMER")) {
for (i in 1..10000) {
//上下文调度器本来就是多个协程可以启动
launch {
count++
}
}
}
TimeUnit.SECONDS.sleep(5)
println(">>>>>count:${count}")
//------------------------------------test2-------------------------------------
var limitedParallelismCounter = 0
actorScope.launch(Dispatcher.Actor.limitedParallelism(1) + CoroutineName("limitedParallelism")) {
for (i in 1..10000) {
// 默认使用父协程上下文(也就是并发度也是1的调度器,起多个协程业没用)
launch {
limitedParallelismCounter++
}
}
}
TimeUnit.SECONDS.sleep(5)
println("-----limitedParallelismCounter:${limitedParallelismCounter}")
//------------------------------------test3-------------------------------------
var limitedParallelismAnotherCounter = 0
actorScope.launch(Dispatcher.Actor.limitedParallelism(1) + CoroutineName("limitedParallelism")) {
for (i in 1..10000) {
// 换了上下文(也就是又变成多线程了)
schedulerScope.launch {
limitedParallelismAnotherCounter++
}
}
}
TimeUnit.SECONDS.sleep(5)
println("=====limitedParallelismAnotherCounter:${limitedParallelismAnotherCounter}")
//------------------------------------test4-------------------------------------
var limitedParallelismSameCounter = 0
actorScope.launch(Dispatcher.Actor.limitedParallelism(1) + CoroutineName("limitedParallelism")) {
for (i in 1..10000) {
// 其实协程作用阈还是变了
actorScope.launch {
limitedParallelismSameCounter++
}
}
}
TimeUnit.SECONDS.sleep(5)
println("*****limitedParallelismAnotherCounter:${limitedParallelismSameCounter}")
}
/*
15:22:27.868 [main] INFO com.example.demo.process.Dispatcher -- create dispatcher Scheduler, corePoolSize=2, maximumPoolSize=8
15:22:27.873 [main] INFO com.example.demo.process.Dispatcher -- create dispatcher Actor, corePoolSize=16, maximumPoolSize=32
>>>>>count:9974
-----limitedParallelismCounter:10000
=====limitedParallelismAnotherCounter:9969
*****limitedParallelismAnotherCounter:9962
*/
总结:
actorScope.launch(Dispatcher.Actor.limitedParallelism(1) + CoroutineName("limitedParallelism"))
通过传入协程上下文,限制该协程下最多只有一个协程在运行,这样子实现Actor模型