最近使用 vert.x 在开发一个 java 程序,只所以用 vert.x 就是因为它比较适合开发这类的程序。但是异步编程开发起来实在不好维护。。实现也太麻烦了,实在是太复杂了。下单完还要判断是否成功,还有循环下单等,很多时候异步实现起来非常的不好实现和阅读。
二、引入虚拟线程
目前引入的虚拟线程遇到非常大的麻烦。一般一个 verticle 绑定一个 eventloop 线程,我把虚拟线程绑定 vert.x eventloop 的线程中,,这样每个 verticle 内使用的所有虚拟线程也是它们自己平台 eventloop 线程,也就是在 verticle 使用的所有虚拟线程和自己平台线程 都是同一个线程,所以理论也是线程安全的。目前遇到的是虚拟线程和它自己的平台线程在执行 log 日志输出的时候,就会遇到死锁。也就是虚拟线程和它自己的平台线程发生了竞争 log.info 的输出。
下面是异常日志:
Thread Thread[#63,vert.x-eventloop-thread-1,5,main] has been blocked for 10586 ms, time limit is 2000 ms
io.vertx.core.VertxException: Thread blocked
at java.base/jdk.internal.misc.Unsafe.park(Native Method)
at java.base/java.util.concurrent.locks.LockSupport.park(LockSupport.java:221)
at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:754)
at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:990)
at java.base/java.util.concurrent.locks.ReentrantLock$Sync.lock(ReentrantLock.java:153)
at java.base/java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:322)
at ch.qos.logback.core.OutputStreamAppender.writeBytes(OutputStreamAppender.java:200)
at ch.qos.logback.core.OutputStreamAppender.writeOut(OutputStreamAppender.java:193)
at ch.qos.logback.core.OutputStreamAppender.subAppend(OutputStreamAppender.java:228)
at ch.qos.logback.core.OutputStreamAppender.append(OutputStreamAppender.java:102)
at ch.qos.logback.core.UnsynchronizedAppenderBase.doAppend(UnsynchronizedAppenderBase.java:85)
at ch.qos.logback.core.spi.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:51)
at ch.qos.logback.classic.Logger.appendLoopOnAppenders(Logger.java:272)
at ch.qos.logback.classic.Logger.callAppenders(Logger.java:259)
at ch.qos.logback.classic.Logger.buildLoggingEventAndAppend(Logger.java:426)
使用 jstack -l pid 检查确实一直 block 中.
LockSupport.class
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
try {
if (t.isVirtual()) {
VirtualThreads.park();
} else {
U.park(false, 0L);
}
} finally {
setBlocker(t, null);
}
}
由于 verticle 可能是很多个,可能会绑定到同一个 eventloop 线程,所以很难从代码上比较难规避。
一般使用 ReentrantLock.lock 产生死锁的情况是什么原因呢?个人感觉跟 vert.x 关系不大,应该是虚拟线程的问题。也不确定是否 ReentrantLock.lock 的问题,头大。