在我的项目中,我使用Spring Data Neo4j(3.4.2.RELEASE)和基于AspectJ的"高级映射"模式使用嵌入式数据库。
我还通过@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
在我的应用程序中,如果发生对数据库的并发读/写操作,我遇到了死锁。我没有收到DeadlockDetectedException
但是看到一个线程无限期地等待RWLock
。在此之后不久,所有剩余的线程将在第一个线程上等待,并且应用程序停止,它不会从中恢复。其他线程也可能已经获得锁定,但我无法看到我不确定它。
锁定线程的堆栈跟踪:
Thread [Processing-XXX] (Suspended)
owns: SeasonOfCompetitionRepositoryImpl (id=134)
waiting for: RWLock (id=174)
Object.wait(long) line: not available [native method]
RWLock(Object).wait() line: 502 [local variables unavailable]
RWLock.acquireWriteLock(Object) line: 388
LockManagerImpl.getWriteLock(Object, Object) line: 66
CommunityLockClient.acquireExclusive(Locks$ResourceType, long) line: 116
LockingStatementOperations.relationshipCreate(KernelStatement, int, long, long) line: 287
OperationsFacade.relationshipCreate(int, long, long) line: 866
NodeProxy.createRelationshipTo(Node, RelationshipType) line: 559
DelegatingGraphDatabase.getOrCreateRelationship(Node, Node, RelationshipType, Direction, Map<String,Object>) line: 298
Neo4jTemplate.getOrCreateRelationship(Node, Node, RelationshipType, Direction, Map<String,Object>) line: 772
RelationshipHelper.createSingleRelationship(Node, Node) line: 198
RelationshipHelper.createAddedRelationships(Node, Set<Node>) line: 154
RelatedToSingleFieldAccessorFactory$RelatedToSingleFieldAccessor(RelatedToFieldAccessor).createAddedRelationships(Node, Set<Node>) line: 78
RelatedToSingleFieldAccessorFactory$RelatedToSingleFieldAccessor.setValue(Object, Object, MappingPolicy) line: 68
NodeEntityState(DefaultEntityState<STATE>).setValue(Neo4jPersistentProperty, Object, MappingPolicy) line: 113
DetachedEntityState<STATE>.setValue(Neo4jPersistentProperty, Object, MappingPolicy) line: 181
DetachedEntityState<STATE>.setValue(Field, Object, MappingPolicy) line: 145
SeasonOfCompetition.season_aroundBody21$advice(SeasonOfCompetition, SeasonOfCompetition, Season, JoinPoint, Neo4jNodeBacking, NodeBacked, Object, AroundClosure, JoinPoint) line: 266
SeasonOfCompetition.setSeason_aroundBody22(SeasonOfCompetition, Season) line: 108
SeasonOfCompetition$AjcClosure23.run(Object[]) line: 1
AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(Object, AroundClosure) line: 66
AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation() line: 72
AnnotationTransactionAspect(TransactionAspectSupport).invokeWithinTransaction(Method, Class<?>, InvocationCallback) line: 281
AnnotationTransactionAspect(AbstractTransactionAspect).ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(Object, AroundClosure, JoinPoint$StaticPart) line: 70
SeasonOfCompetition.setSeason(Season) line: 95
SeasonOfCompetitionRepositoryImpl.getOrCreate_aroundBody0(SeasonOfCompetitionRepositoryImpl, Season, Competition) line: 32
SeasonOfCompetitionRepositoryImpl$AjcClosure1.run(Object[]) line: 1
AbstractTransactionAspect.ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96cproceed(Object, AroundClosure) line: 66
AbstractTransactionAspect$AbstractTransactionAspect$1.proceedWithInvocation() line: 72
AnnotationTransactionAspect(TransactionAspectSupport).invokeWithinTransaction(Method, Class<?>, InvocationCallback) line: 281
AnnotationTransactionAspect(AbstractTransactionAspect).ajc$around$org_springframework_transaction_aspectj_AbstractTransactionAspect$1$2a73e96c(Object, AroundClosure, JoinPoint$StaticPart) line: 70
SeasonOfCompetitionRepositoryImpl.getOrCreate(Season, Competition) line: 26
这是自定义RepositoryExtension的一部分,它应该为给定的参数创建唯一的SeasonOfCompetition
:
@Override
@Transactional
public synchronized SeasonOfCompetition getOrCreate(Season season, Competition competition)
{
String uri = buildUri(season, competition);
SeasonOfCompetition soc = getOrCreateByUri(uri);
if(soc.getSeason() == null)
{
soc.setSeason(season);
}
if(soc.getCompetition() == null)
{
soc.setCompetition(competition);
}
return soc;
}
请注意,我已经使用@Transactional
和sychronized
玩了一下 - 我最初都没有使用它们,因为我认为(但不确定)他们这里不需要。
这是线程卡住的域实体SeasonOfCompetition
的选择:
@NodeEntity
public class SeasonOfCompetition extends UriEntity
{
@RelatedTo(type = "inSeason", direction = Direction.OUTGOING)
private Season season;
@Transactional
public void setSeason(Season season)
{
this.season = season;
}
}
我在这里做错了什么?我假设通过SDN访问图形数据库是线程安全的,并且除了使用事务之外我不需要任何特殊处理。
关于如何调试此建议或如何使其在此情况下抛出DeadlockDetectedException
的建议也是受欢迎的。
我以前也有其他与并发相关的问题(重复的实体是唯一的,重复的关系等),并没有完全了解我还需要处理这个问题。关于如何为NodeEntities和Relationships实现线程安全的#getOrCreate的输入值得赞赏。
编辑:
我已尝试实施基于MERGE
- 查询的#getOrCreate版本,该版本未使用@cybersam建议的任何自定义同步:
@Override
@Transactional
@SuppressWarnings("deprecation")
public T getOrCreateByUri(String uri)
{
checkArgument(!StringUtils.isEmpty(uri));
Class<T> clazz = getEntityType();
String queryString = "MERGE (n:" + clazz.getSimpleName() + " {uri: {uri}}) RETURN n";
Map<String, Object> parameters = new HashMap<>();
parameters.put("uri", uri);
Node node = (Node) template.getGraphDatabase().queryEngine().query(queryString, parameters).single().get( "n" );
template.postEntityCreation(node, clazz);
T result = (T) template.convert(node, clazz);
return result.persist();
}
然而,这无法创建唯一的节点。创建100个线程并并行调用它的简单测试失败,因为创建了多个节点。
这可能与result.persist()
有关,但如果没有,则返回的实体不会附加到图表中,并且缺少某些信息。
答案 0 :(得分:1)
neo4j documentation包含此警告:
执行Neo4j操作的重要强>
使用除锁之外的其他同步导致的死锁 由Neo4j管理仍然可以发生。自从Neo4j中的所有操作 除非另有说明,否则API是线程安全的,不需要 外部同步。其他需要同步的代码 应该以一种从不执行任何Neo4j的方式进行同步 同步块中的操作。
getOrCreate()
目前为synchronized
。因此,死锁可能实际上是由您自己的代码的Java同步引起的。您需要重新设计代码,以避免在执行Neo4j操作时使用Java同步。