Spring Boot 和 H2 关闭死锁
Springboot and H2 shutdown deadlock
目前我可以在结合使用 h2 和 spring 引导时创建死锁。那里有很多问题,看起来有点相似,但显然它们已经解决了,我不完全确定是 Spring 还是 h2 做了一些奇怪的事情。
首先关闭死锁的 visualvm 跟踪(滚动到锁的底部):
2019-07-08 12:34:47
Full thread dump OpenJDK 64-Bit Server VM (25.212-b03 mixed mode):
"RMI TCP Connection(2)-127.0.0.1" #25 daemon prio=9 os_prio=0 tid=0x00007fc19c0f6000 nid=0x2f44 runnable [0x00007fc1a2444000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x00000000f5c5bdb0> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run[=10=](TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda7/422104739.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- <0x00000000f58d4990> (a java.util.concurrent.ThreadPoolExecutor$Worker)
"JMX server connection timeout 24" #24 daemon prio=9 os_prio=0 tid=0x00007fc19804c000 nid=0x2f42 in Object.wait() [0x00007fc1a2746000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000f59cafe0> (a [I)
at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
- locked <0x00000000f59cafe0> (a [I)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
"RMI Scheduler(0)" #23 daemon prio=9 os_prio=0 tid=0x00007fc198039800 nid=0x2f41 waiting on condition [0x00007fc1a32fd000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f5586878> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
"RMI TCP Connection(1)-127.0.0.1" #22 daemon prio=9 os_prio=0 tid=0x00007fc19c57d800 nid=0x2f40 runnable [0x00007fc1a3dfd000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x00000000f598e720> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run[=10=](TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda7/422104739.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- <0x00000000f58d4360> (a java.util.concurrent.ThreadPoolExecutor$Worker)
"RMI TCP Accept-0" #21 daemon prio=9 os_prio=0 tid=0x00007fc1ad3a9800 nid=0x2f3e runnable [0x00007fc1b47fb000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at sun.management.jmxremote.LocalRMIServerSocketFactory.accept(LocalRMIServerSocketFactory.java:52)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:405)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:377)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
"Attach Listener" #20 daemon prio=9 os_prio=0 tid=0x00007fc1c8001000 nid=0x2f3c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Thread-8" #19 prio=5 os_prio=0 tid=0x00007fc210870800 nid=0x2f06 waiting for monitor entry [0x00007fc1b4ffc000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:98)
- waiting to lock <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.command.CommandContainer.update(CommandContainer.java:133)
at org.h2.command.Command.executeUpdate(Command.java:267)
- locked <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:233)
- locked <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:205)
at org.springframework.jdbc.datasource.embedded.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:47)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory.shutdownDatabase(EmbeddedDatabaseFactory.java:228)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy.shutdown(EmbeddedDatabaseFactory.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1027)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026)
at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:945)
- locked <0x00000000ff2b55d0> (a java.lang.Object)
Locked ownable synchronizers:
- None
"Thread-6" #17 prio=5 os_prio=0 tid=0x00007fc2107e7000 nid=0x2f05 waiting for monitor entry [0x00007fc1b4efb000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:98)
- waiting to lock <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.command.CommandContainer.update(CommandContainer.java:133)
at org.h2.command.Command.executeUpdate(Command.java:267)
- locked <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:233)
- locked <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:205)
at org.springframework.jdbc.datasource.embedded.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:47)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory.shutdownDatabase(EmbeddedDatabaseFactory.java:228)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy.shutdown(EmbeddedDatabaseFactory.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1027)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026)
at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:945)
- locked <0x00000000e002b8d0> (a java.lang.Object)
Locked ownable synchronizers:
- None
"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007fc210169800 nid=0x2ef5 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007fc210156000 nid=0x2ef4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007fc210154000 nid=0x2ef3 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fc210152000 nid=0x2ef2 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fc210150000 nid=0x2ef1 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fc21014d800 nid=0x2ef0 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fc21008c800 nid=0x2eef in Object.wait() [0x00007fc1e160c000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e0016c68> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x00000000e0016c68> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
Locked ownable synchronizers:
- None
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fc21008a000 nid=0x2eee in Object.wait() [0x00007fc1e170d000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e0016e38> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000e0016e38> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
Locked ownable synchronizers:
- None
"main" #1 prio=5 os_prio=0 tid=0x00007fc21000b000 nid=0x2ee4 in Object.wait() [0x00007fc214f53000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000ff24cda8> (a org.springframework.context.support.AbstractApplicationContext)
at java.lang.Thread.join(Thread.java:1252)
- locked <0x00000000ff24cda8> (a org.springframework.context.support.AbstractApplicationContext)
at java.lang.Thread.join(Thread.java:1326)
at java.lang.ApplicationShutdownHooks.runHooks(ApplicationShutdownHooks.java:107)
at java.lang.ApplicationShutdownHooks.run(ApplicationShutdownHooks.java:46)
at java.lang.Shutdown.runHooks(Shutdown.java:123)
at java.lang.Shutdown.sequence(Shutdown.java:167)
at java.lang.Shutdown.exit(Shutdown.java:212)
- locked <0x00000000e039fcc8> (a java.lang.Class for java.lang.Shutdown)
at java.lang.Runtime.exit(Runtime.java:109)
at java.lang.System.exit(System.java:971)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:68)
Locked ownable synchronizers:
- None
"VM Thread" os_prio=0 tid=0x00007fc210080000 nid=0x2eed runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fc210020000 nid=0x2ee5 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fc210021800 nid=0x2ee6 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007fc210023800 nid=0x2ee7 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007fc210025000 nid=0x2ee8 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007fc210027000 nid=0x2ee9 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007fc210028800 nid=0x2eea runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007fc21002a800 nid=0x2eeb runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007fc21002c000 nid=0x2eec runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007fc21016c000 nid=0x2ef6 waiting on condition
JNI global references: 1324
Found one Java-level deadlock:
=============================
"Thread-8":
waiting to lock monitor 0x00007fc1ad3792c8 (object 0x00000000ff487080, a org.h2.engine.Session),
which is held by "Thread-6"
"Thread-6":
waiting to lock monitor 0x00007fc1ad377cc8 (object 0x00000000ff5dc0d0, a org.h2.engine.Session),
which is held by "Thread-8"
Java stack information for the threads listed above:
===================================================
"Thread-8":
at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:98)
- waiting to lock <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.command.CommandContainer.update(CommandContainer.java:133)
at org.h2.command.Command.executeUpdate(Command.java:267)
- locked <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:233)
- locked <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:205)
at org.springframework.jdbc.datasource.embedded.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:47)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory.shutdownDatabase(EmbeddedDatabaseFactory.java:228)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy.shutdown(EmbeddedDatabaseFactory.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1027)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026)
at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:945)
- locked <0x00000000ff2b55d0> (a java.lang.Object)
"Thread-6":
at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:98)
- waiting to lock <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.command.CommandContainer.update(CommandContainer.java:133)
at org.h2.command.Command.executeUpdate(Command.java:267)
- locked <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:233)
- locked <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:205)
at org.springframework.jdbc.datasource.embedded.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:47)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory.shutdownDatabase(EmbeddedDatabaseFactory.java:228)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy.shutdown(EmbeddedDatabaseFactory.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1027)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026)
at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:945)
- locked <0x00000000e002b8d0> (a java.lang.Object)
Found 1 deadlock.
跟踪锁定跟踪中的文件时,很明显它在关机期间锁定。为了重现,设置了以下代码:
Application.kt
package com.example.deadlock
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.jdbc.core.JdbcTemplate
@SpringBootApplication
@Configuration
open class Application {
@Bean
open fun repository(jdbcTemplate: JdbcTemplate): Repository {
return Repository(jdbcTemplate)
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
}
}
Repository.kt
package com.example.deadlock
import org.springframework.jdbc.core.JdbcTemplate
class Repository constructor(private val template: JdbcTemplate) {
fun findAll(): List<String> =
template.queryForList("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='jfdksaiufd'", String::class.java)
}
Controller.kt
package com.example.deadlock
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping(path = ["/foo"],
produces = [MediaType.APPLICATION_JSON_UTF8_VALUE])
class Controller(private val repository: Repository) {
@GetMapping
fun banks(): List<String> {
val banks = repository.findAll()
return emptyList()
}
}
要重现错误,以下测试组合似乎是最低限度的可用:
EndpointTest.kt
package com.example.deadlock
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
@SpringBootTest(classes = [Application::class])
@AutoConfigureMockMvc
class EndpointTest @Autowired constructor(private val mockMvc: MockMvc) {
@Test
fun empty() {
val mvcRequest = MockMvcRequestBuilders.get("/foo").accept(MediaType.APPLICATION_JSON_UTF8)
mockMvc.perform(mvcRequest)
.andExpect(status().isOk)
.andExpect(content().string("[]"))
}
}
RepositoryTest.kt
package com.example.deadlock
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import kotlin.test.assertEquals
@SpringBootTest(classes = [Application::class])
class RepositoryTest @Autowired constructor(private val repository: Repository) {
@Test
fun initialCount() {
assertEquals(0, repository.findAll().size)
}
}
为了完整起见,这是有问题的 build.gradle:
buildscript {
ext {
kotlinVersion = "1.3.31"
springVersion = "2.1.5.RELEASE"
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springVersion}"
}
}
plugins {
id "org.jetbrains.kotlin.jvm" version "1.3.31"
id "org.springframework.boot" version "2.1.5.RELEASE"
id "idea"
}
apply plugin: "org.springframework.boot"
apply plugin: "io.spring.dependency-management"
group = "com.example.deadlock"
version = "0.0.1-SNAPSHOT"
description = "Deadlock with Spring and h2"
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
compileJava.options.encoding = "UTF-8"
tasks.withType(Test) {
useJUnitPlatform()
outputs.upToDateWhen {false}
}
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlin:kotlin-reflect"
testImplementation "org.jetbrains.kotlin:kotlin-test"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework:spring-jdbc"
compile "com.h2database:h2"
// Test setups
testCompile "org.junit.jupiter:junit-jupiter-api"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine"
testCompile "org.springframework.boot:spring-boot-starter-test"
}
而且至少在我的机器上它很容易被 运行 ./gradlew clean assemble
重现,然后循环 ./gradlew test
.
出于某种原因,如果两个测试完全相同,它就不起作用,而且(不足为奇)我无法仅通过一个测试重现。
据我所知,似乎有两个关闭挂钩最终会竞争关闭问题。但是,我并不完全确定。
在询问后不久,我当然发现了这个:https://github.com/h2database/h2database/issues/1841
看起来确实是同一个问题。我现在已经将 @DirtiesContext
添加到两个测试中,目前它们已经 运行 超过 15 次没有问题
目前我可以在结合使用 h2 和 spring 引导时创建死锁。那里有很多问题,看起来有点相似,但显然它们已经解决了,我不完全确定是 Spring 还是 h2 做了一些奇怪的事情。
首先关闭死锁的 visualvm 跟踪(滚动到锁的底部):
2019-07-08 12:34:47
Full thread dump OpenJDK 64-Bit Server VM (25.212-b03 mixed mode):
"RMI TCP Connection(2)-127.0.0.1" #25 daemon prio=9 os_prio=0 tid=0x00007fc19c0f6000 nid=0x2f44 runnable [0x00007fc1a2444000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x00000000f5c5bdb0> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run[=10=](TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda7/422104739.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- <0x00000000f58d4990> (a java.util.concurrent.ThreadPoolExecutor$Worker)
"JMX server connection timeout 24" #24 daemon prio=9 os_prio=0 tid=0x00007fc19804c000 nid=0x2f42 in Object.wait() [0x00007fc1a2746000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000f59cafe0> (a [I)
at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
- locked <0x00000000f59cafe0> (a [I)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
"RMI Scheduler(0)" #23 daemon prio=9 os_prio=0 tid=0x00007fc198039800 nid=0x2f41 waiting on condition [0x00007fc1a32fd000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000f5586878> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
"RMI TCP Connection(1)-127.0.0.1" #22 daemon prio=9 os_prio=0 tid=0x00007fc19c57d800 nid=0x2f40 runnable [0x00007fc1a3dfd000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read(BufferedInputStream.java:265)
- locked <0x00000000f598e720> (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(FilterInputStream.java:83)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:555)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run[=10=](TCPTransport.java:688)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler$$Lambda7/422104739.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- <0x00000000f58d4360> (a java.util.concurrent.ThreadPoolExecutor$Worker)
"RMI TCP Accept-0" #21 daemon prio=9 os_prio=0 tid=0x00007fc1ad3a9800 nid=0x2f3e runnable [0x00007fc1b47fb000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at sun.management.jmxremote.LocalRMIServerSocketFactory.accept(LocalRMIServerSocketFactory.java:52)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(TCPTransport.java:405)
at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(TCPTransport.java:377)
at java.lang.Thread.run(Thread.java:748)
Locked ownable synchronizers:
- None
"Attach Listener" #20 daemon prio=9 os_prio=0 tid=0x00007fc1c8001000 nid=0x2f3c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Thread-8" #19 prio=5 os_prio=0 tid=0x00007fc210870800 nid=0x2f06 waiting for monitor entry [0x00007fc1b4ffc000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:98)
- waiting to lock <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.command.CommandContainer.update(CommandContainer.java:133)
at org.h2.command.Command.executeUpdate(Command.java:267)
- locked <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:233)
- locked <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:205)
at org.springframework.jdbc.datasource.embedded.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:47)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory.shutdownDatabase(EmbeddedDatabaseFactory.java:228)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy.shutdown(EmbeddedDatabaseFactory.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1027)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026)
at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:945)
- locked <0x00000000ff2b55d0> (a java.lang.Object)
Locked ownable synchronizers:
- None
"Thread-6" #17 prio=5 os_prio=0 tid=0x00007fc2107e7000 nid=0x2f05 waiting for monitor entry [0x00007fc1b4efb000]
java.lang.Thread.State: BLOCKED (on object monitor)
at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:98)
- waiting to lock <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.command.CommandContainer.update(CommandContainer.java:133)
at org.h2.command.Command.executeUpdate(Command.java:267)
- locked <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:233)
- locked <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:205)
at org.springframework.jdbc.datasource.embedded.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:47)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory.shutdownDatabase(EmbeddedDatabaseFactory.java:228)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy.shutdown(EmbeddedDatabaseFactory.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1027)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026)
at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:945)
- locked <0x00000000e002b8d0> (a java.lang.Object)
Locked ownable synchronizers:
- None
"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00007fc210169800 nid=0x2ef5 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00007fc210156000 nid=0x2ef4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00007fc210154000 nid=0x2ef3 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007fc210152000 nid=0x2ef2 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007fc210150000 nid=0x2ef1 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007fc21014d800 nid=0x2ef0 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007fc21008c800 nid=0x2eef in Object.wait() [0x00007fc1e160c000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e0016c68> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x00000000e0016c68> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
Locked ownable synchronizers:
- None
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007fc21008a000 nid=0x2eee in Object.wait() [0x00007fc1e170d000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000e0016e38> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000e0016e38> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
Locked ownable synchronizers:
- None
"main" #1 prio=5 os_prio=0 tid=0x00007fc21000b000 nid=0x2ee4 in Object.wait() [0x00007fc214f53000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000ff24cda8> (a org.springframework.context.support.AbstractApplicationContext)
at java.lang.Thread.join(Thread.java:1252)
- locked <0x00000000ff24cda8> (a org.springframework.context.support.AbstractApplicationContext)
at java.lang.Thread.join(Thread.java:1326)
at java.lang.ApplicationShutdownHooks.runHooks(ApplicationShutdownHooks.java:107)
at java.lang.ApplicationShutdownHooks.run(ApplicationShutdownHooks.java:46)
at java.lang.Shutdown.runHooks(Shutdown.java:123)
at java.lang.Shutdown.sequence(Shutdown.java:167)
at java.lang.Shutdown.exit(Shutdown.java:212)
- locked <0x00000000e039fcc8> (a java.lang.Class for java.lang.Shutdown)
at java.lang.Runtime.exit(Runtime.java:109)
at java.lang.System.exit(System.java:971)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:68)
Locked ownable synchronizers:
- None
"VM Thread" os_prio=0 tid=0x00007fc210080000 nid=0x2eed runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007fc210020000 nid=0x2ee5 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007fc210021800 nid=0x2ee6 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007fc210023800 nid=0x2ee7 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007fc210025000 nid=0x2ee8 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00007fc210027000 nid=0x2ee9 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00007fc210028800 nid=0x2eea runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00007fc21002a800 nid=0x2eeb runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00007fc21002c000 nid=0x2eec runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00007fc21016c000 nid=0x2ef6 waiting on condition
JNI global references: 1324
Found one Java-level deadlock:
=============================
"Thread-8":
waiting to lock monitor 0x00007fc1ad3792c8 (object 0x00000000ff487080, a org.h2.engine.Session),
which is held by "Thread-6"
"Thread-6":
waiting to lock monitor 0x00007fc1ad377cc8 (object 0x00000000ff5dc0d0, a org.h2.engine.Session),
which is held by "Thread-8"
Java stack information for the threads listed above:
===================================================
"Thread-8":
at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:98)
- waiting to lock <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.command.CommandContainer.update(CommandContainer.java:133)
at org.h2.command.Command.executeUpdate(Command.java:267)
- locked <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:233)
- locked <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:205)
at org.springframework.jdbc.datasource.embedded.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:47)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory.shutdownDatabase(EmbeddedDatabaseFactory.java:228)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy.shutdown(EmbeddedDatabaseFactory.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1027)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026)
at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:945)
- locked <0x00000000ff2b55d0> (a java.lang.Object)
"Thread-6":
at org.h2.command.dml.TransactionCommand.update(TransactionCommand.java:98)
- waiting to lock <0x00000000ff5dc0d0> (a org.h2.engine.Session)
at org.h2.command.CommandContainer.update(CommandContainer.java:133)
at org.h2.command.Command.executeUpdate(Command.java:267)
- locked <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.executeInternal(JdbcStatement.java:233)
- locked <0x00000000ff487080> (a org.h2.engine.Session)
at org.h2.jdbc.JdbcStatement.execute(JdbcStatement.java:205)
at org.springframework.jdbc.datasource.embedded.AbstractEmbeddedDatabaseConfigurer.shutdown(AbstractEmbeddedDatabaseConfigurer.java:47)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory.shutdownDatabase(EmbeddedDatabaseFactory.java:228)
at org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy.shutdown(EmbeddedDatabaseFactory.java:303)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1034)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1027)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1057)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1026)
at org.springframework.context.support.AbstractApplicationContext.run(AbstractApplicationContext.java:945)
- locked <0x00000000e002b8d0> (a java.lang.Object)
Found 1 deadlock.
跟踪锁定跟踪中的文件时,很明显它在关机期间锁定。为了重现,设置了以下代码:
Application.kt
package com.example.deadlock
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.jdbc.core.JdbcTemplate
@SpringBootApplication
@Configuration
open class Application {
@Bean
open fun repository(jdbcTemplate: JdbcTemplate): Repository {
return Repository(jdbcTemplate)
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
}
}
Repository.kt
package com.example.deadlock
import org.springframework.jdbc.core.JdbcTemplate
class Repository constructor(private val template: JdbcTemplate) {
fun findAll(): List<String> =
template.queryForList("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='jfdksaiufd'", String::class.java)
}
Controller.kt
package com.example.deadlock
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping(path = ["/foo"],
produces = [MediaType.APPLICATION_JSON_UTF8_VALUE])
class Controller(private val repository: Repository) {
@GetMapping
fun banks(): List<String> {
val banks = repository.findAll()
return emptyList()
}
}
要重现错误,以下测试组合似乎是最低限度的可用:
EndpointTest.kt
package com.example.deadlock
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.content
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
@SpringBootTest(classes = [Application::class])
@AutoConfigureMockMvc
class EndpointTest @Autowired constructor(private val mockMvc: MockMvc) {
@Test
fun empty() {
val mvcRequest = MockMvcRequestBuilders.get("/foo").accept(MediaType.APPLICATION_JSON_UTF8)
mockMvc.perform(mvcRequest)
.andExpect(status().isOk)
.andExpect(content().string("[]"))
}
}
RepositoryTest.kt
package com.example.deadlock
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import kotlin.test.assertEquals
@SpringBootTest(classes = [Application::class])
class RepositoryTest @Autowired constructor(private val repository: Repository) {
@Test
fun initialCount() {
assertEquals(0, repository.findAll().size)
}
}
为了完整起见,这是有问题的 build.gradle:
buildscript {
ext {
kotlinVersion = "1.3.31"
springVersion = "2.1.5.RELEASE"
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springVersion}"
}
}
plugins {
id "org.jetbrains.kotlin.jvm" version "1.3.31"
id "org.springframework.boot" version "2.1.5.RELEASE"
id "idea"
}
apply plugin: "org.springframework.boot"
apply plugin: "io.spring.dependency-management"
group = "com.example.deadlock"
version = "0.0.1-SNAPSHOT"
description = "Deadlock with Spring and h2"
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
compileJava.options.encoding = "UTF-8"
tasks.withType(Test) {
useJUnitPlatform()
outputs.upToDateWhen {false}
}
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlin:kotlin-reflect"
testImplementation "org.jetbrains.kotlin:kotlin-test"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
compile "org.springframework.boot:spring-boot-starter-web"
compile "org.springframework:spring-jdbc"
compile "com.h2database:h2"
// Test setups
testCompile "org.junit.jupiter:junit-jupiter-api"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine"
testCompile "org.springframework.boot:spring-boot-starter-test"
}
而且至少在我的机器上它很容易被 运行 ./gradlew clean assemble
重现,然后循环 ./gradlew test
.
出于某种原因,如果两个测试完全相同,它就不起作用,而且(不足为奇)我无法仅通过一个测试重现。
据我所知,似乎有两个关闭挂钩最终会竞争关闭问题。但是,我并不完全确定。
在询问后不久,我当然发现了这个:https://github.com/h2database/h2database/issues/1841
看起来确实是同一个问题。我现在已经将 @DirtiesContext
添加到两个测试中,目前它们已经 运行 超过 15 次没有问题