在触发应用程序事件时创建 bean 时如何避免 Spring 中的死锁?
How can i avoid a deadlock in Spring when creating a bean while triggering an application event?
当一个线程在 mongo 数据库上执行查询时,我的应用程序发生死锁,导致应用程序事件被触发,而另一个线程试图用 AutowireCapableBeanFactory
创建一个 bean。
这发生在 @Service 的构造函数中,并阻止启动整个应用程序,因为服务是在启动时创建的。
伪代码:
@Service
public class MyService {
@Autowired
public MyService(AutowireCapableBeanFactory factory, TaskScheduler scheduler) {
MyFirstCommand command1 = factory.getBean(MyFirstCommand.class);
scheduler.schedule(command1, new Date());
MySecondCommand command2 = factory.getBean(MySecondCommand.class);
scheduler.schedule(command2, new Date());
}
}
public class MyFirstCommand extends Runnable {
@Autowired MongoOperations mongo;
@Override
public void run() {
mongo.findById("some-id", MyData.class);
}
}
请注意,我有一些 AbstractMongoEventListener
注册为 @Component
。
MyFirstCommand.run()
中的一个线程执行 mongo.findById()
时发生死锁。另一个线程在 MyService
的构造函数中执行 factory.getBean(MySecondCommand.class):
Found one Java-level deadlock:
=============================
"pool-1-thread-1":
waiting to lock monitor 0x00007fd05d5bf5d8 (object 0x00000000816d0970, a java.util.concurrent.ConcurrentHashMap),
which is held by "main"
"main":
waiting for ownable synchronizer 0x0000000081ed8468, (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync),
which is held by "pool-1-thread-1"
Java stack information for the threads listed above:
===================================================
"pool-1-thread-1":
at org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:179)
- waiting to lock <0x00000000816d0970> (a java.util.concurrent.ConcurrentHashMap)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:381)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:335)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:326)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:179)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:139)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:66)
at org.springframework.data.mongodb.core.MongoTemplate.determineCollectionName(MongoTemplate.java:1976)
at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:607)
"main":
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000081ed8468> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSynchronizer.java:967)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.java:1283)
at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lock(ReentrantReadWriteLock.java:727)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:160)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:139)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:66)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactory.getEntityInformation(MongoRepositoryFactory.java:118)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactory.getTargetRepository(MongoRepositoryFactory.java:92)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:185)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:251)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:237)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean.afterPropertiesSet(MongoRepositoryFactoryBean.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
为什么Spring 这里不是线程安全的?我该怎么做才能避免这种僵局?
此死锁是由于 Spring 中的错误造成的。我在他们的 JIRA 中创建了一个问题:DATACMNS-901.
作为解决方法,我现在在安排所有可运行 bean 之前创建它们:
@Autowired
public MyService(AutowireCapableBeanFactory factory, TaskScheduler scheduler) {
MyFirstCommand command1 = factory.getBean(MyFirstCommand.class);
MySecondCommand command2 = factory.getBean(MySecondCommand.class);
scheduler.schedule(command2, new Date());
scheduler.schedule(command1, new Date());
}
当一个线程在 mongo 数据库上执行查询时,我的应用程序发生死锁,导致应用程序事件被触发,而另一个线程试图用 AutowireCapableBeanFactory
创建一个 bean。
这发生在 @Service 的构造函数中,并阻止启动整个应用程序,因为服务是在启动时创建的。
伪代码:
@Service
public class MyService {
@Autowired
public MyService(AutowireCapableBeanFactory factory, TaskScheduler scheduler) {
MyFirstCommand command1 = factory.getBean(MyFirstCommand.class);
scheduler.schedule(command1, new Date());
MySecondCommand command2 = factory.getBean(MySecondCommand.class);
scheduler.schedule(command2, new Date());
}
}
public class MyFirstCommand extends Runnable {
@Autowired MongoOperations mongo;
@Override
public void run() {
mongo.findById("some-id", MyData.class);
}
}
请注意,我有一些 AbstractMongoEventListener
注册为 @Component
。
MyFirstCommand.run()
中的一个线程执行 mongo.findById()
时发生死锁。另一个线程在 MyService
的构造函数中执行 factory.getBean(MySecondCommand.class):
Found one Java-level deadlock:
=============================
"pool-1-thread-1":
waiting to lock monitor 0x00007fd05d5bf5d8 (object 0x00000000816d0970, a java.util.concurrent.ConcurrentHashMap),
which is held by "main"
"main":
waiting for ownable synchronizer 0x0000000081ed8468, (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync),
which is held by "pool-1-thread-1"
Java stack information for the threads listed above:
===================================================
"pool-1-thread-1":
at org.springframework.context.event.AbstractApplicationEventMulticaster.getApplicationListeners(AbstractApplicationEventMulticaster.java:179)
- waiting to lock <0x00000000816d0970> (a java.util.concurrent.ConcurrentHashMap)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:381)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:335)
at org.springframework.data.mapping.context.AbstractMappingContext.addPersistentEntity(AbstractMappingContext.java:326)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:179)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:139)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:66)
at org.springframework.data.mongodb.core.MongoTemplate.determineCollectionName(MongoTemplate.java:1976)
at org.springframework.data.mongodb.core.MongoTemplate.findById(MongoTemplate.java:607)
"main":
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000081ed8468> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireShared(AbstractQueuedSynchronizer.java:967)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireShared(AbstractQueuedSynchronizer.java:1283)
at java.util.concurrent.locks.ReentrantReadWriteLock$ReadLock.lock(ReentrantReadWriteLock.java:727)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:160)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:139)
at org.springframework.data.mapping.context.AbstractMappingContext.getPersistentEntity(AbstractMappingContext.java:66)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactory.getEntityInformation(MongoRepositoryFactory.java:118)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactory.getTargetRepository(MongoRepositoryFactory.java:92)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:185)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:251)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:237)
at org.springframework.data.mongodb.repository.support.MongoRepositoryFactoryBean.afterPropertiesSet(MongoRepositoryFactoryBean.java:108)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:545)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
为什么Spring 这里不是线程安全的?我该怎么做才能避免这种僵局?
此死锁是由于 Spring 中的错误造成的。我在他们的 JIRA 中创建了一个问题:DATACMNS-901.
作为解决方法,我现在在安排所有可运行 bean 之前创建它们:
@Autowired
public MyService(AutowireCapableBeanFactory factory, TaskScheduler scheduler) {
MyFirstCommand command1 = factory.getBean(MyFirstCommand.class);
MySecondCommand command2 = factory.getBean(MySecondCommand.class);
scheduler.schedule(command2, new Date());
scheduler.schedule(command1, new Date());
}