Kotlin 使用虚拟线程与小技巧

一、虚拟线程核心概念

1.1 虚拟线程与协程对比

虚拟线程
特性虚拟线程协程(Kotlin/Go)
阻塞行为自动释放平台线程(yielding)非抢占式调度(协作式)
调度机制JVM抢占式调度运行时库协作式调度
系统资源消耗约1KB栈内存约2KB-4KB栈内存
阻塞优化阻塞自动切换线程需手动挂起/让出线程

代码示例:

// 虚拟线程自动释放平台线程
Thread.ofVirtual().start {
    Socket().use { s ->
        s.connect(InetSocketAddress("example.com", 80))
        // 阻塞IO自动让出线程
        val isv = s.getInputStream()
        isv.read()
    }
}

二、虚拟线程创建

2.1 创建

  1. 直接创建执行
Thread.startVirtualThread {
    Thread.currentThread().setName("virtual thread")
}
  1. 通过构造器
// 使用 unstarted 方法创建并启动
val vt = Thread.ofVirtual().unstarted {
    Thread.currentThread().name = "virtual thread"
}
vt.start()

// 直接 start 方法创建
Thread.ofVirtual().start {
    Thread.currentThread().name = "virtual thread"
}
  1. 通过线程工厂
val factory = Thread.ofVirtual().factory()
val vt = factory.newThread {
    Thread.currentThread().name = "virtual thread"
}
vt.start()
  1. 通过线程池
val executor = Executors.newVirtualThreadPerTaskExecutor()
executor.execute {
    Thread.currentThread().name = "virtual thread"
}

2.2 简单封装

suspend fun main() {
    vt {
        Thread.currentThread().setName("virtual thread")
    }
}

fun vt(callback: () -> Unit) {
    Thread.startVirtualThread {
        callback()
    }
}

2.3 创建一个虚拟线程池,如果版本过低则返回null

/**
 * 创建一个虚拟线程池
 */
val virtualThreadExecutor: ExecutorService? by lazy {
    runCatching {
        val clazz = Executors::class.java
        val method = clazz.getMethod("newVirtualThreadPerTaskExecutor")
        method.isAccessible = true
        method.invoke(null) as ExecutorService
    }.getOrNull()
}

2.4 创建一个虚拟线程,如果版本过低则使用协程

/**
 * 启动一个虚拟线程,如果没有虚拟线程则启动一个IO协程
 */
fun <T> virtualThread(block: suspend CoroutineScope.() -> T): Future<T> {
    val future = CompletableFuture<T>()
    if (virtualThreadExecutor != null) {
        return virtualThreadExecutor!!.submit {
            runBlocking(block = block)
        } as Future<T>
    }

    CoroutineScope(Dispatchers.Default).launch {
        kotlin.runCatching { block().apply { future.complete(this) } }.onFailure {
            future.completeExceptionally(it)
        }
    }
    return future
}

2.5 创建一个虚拟线程调度器

kotlin 可以通过 asCoroutineDispatcher 方法将线程池转为一个协程调度器,其中则可以包括虚拟线程池

val VT = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher()

// 使用协程
CoroutineScope(VT).launch {
    println(51)
}

2.6 虚拟线程调度器

三、并发控制优化

3.1 锁优化

不推荐使用 synchronized ,因为 synchronized 会将资源在单一线程中锁起来,导致如果存在多个虚拟线程执行,每个虚拟线程中只有一个能访问到该资源

// 使用synchronized的典型阻塞场景
@Synchronized
fun blockingMethod() {
    // 当前平台线程完全阻塞
    // 其他虚拟线程无法复用该线程资源
    try {
        Thread.sleep(1000)
    } catch (e: InterruptedException) {
    }
}

推荐使用 ReentrantLock 。ReentrantLock,也被称为“可重入锁”,是一个同步工具类,在java.util.concurrent.locks包下。这种锁的一个重要特点是,它允许一个线程多次获取同一个锁而不会产生死锁

private val lock = ReentrantLock()

fun nonBlockingMethod() {
    if (lock.tryLock()) {
        try {
            // 执行临界区代码
        } finally {
            lock.unlock()
        }
    } else {
        // 处理获取失败逻辑
        // 可选择延迟重试或任务转移
    }
}

// 超时机制
fun safeMethod() {
    if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
        try {
            // 安全执行业务逻辑
        } finally {
            lock.unlock()
        }
    } else {
        throw TimeoutException("锁获取超时")
    }
}

3.2 ScopedValue

在 Java 21+ 开始引入了 ScopedValue ,尽管 ScopedValue 在 21 还是预览版本。但是相比 ThreadLocal 还是更加推荐 ScopedValue 。
支持在线程内和线程间共享不可变数据。它们优于线程局部变量,尤其是在使用大量虚拟线程时

// 上下文传递
val USER_CTX = ScopedValue.newInstance<String?>()

fun handleRequest() {
    ScopedValue.where<String?>(USER_CTX, "user123").run(Runnable {})
}

四、最佳实践指南

  1. 避免过度线程本地存储
// 优化前
ThreadLocal<Buffer> buf = ThreadLocal.withInitial(() -> new Buffer(1MB));

// 优化后
class Context {
    Buffer buffer = Buffer(1MB); // 与虚拟线程生命周期绑定
}
  1. 结构化并发模式
ShutdownOnFailure().use { scope ->
    val f1: Future<Int?> = scope.fork<Any?>(this::compute1)
    val f2: Future<Int?> = scope.fork<Any?>(this::compute2)
    scope.join()
    return f1.result() + f2.result()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值