gpt4 book ai didi

Java的synchronized方法并不是同步的

转载 作者:行者123 更新时间:2023-12-02 03:38:59 26 4
gpt4 key购买 nike

我有 JAX-RS、Guice、MyBatis 的项目。有一个通过 REST 端点调用的方法 getToken()。它是同步的,以避免由于 @Transactional(isolation = Isolation.SERIALIZABLE) 而出现异常。但是synchronized方法并不安全,不同的调用可能会同时影响数据并抛出异常:

Cause: org.postgresql.util.PSQLException: ERROR: could not serialize access due to read/write dependencies among transactions

我尝试通过映射器对象进行同步,但它也不起作用。唯一有效的解决方案是删除同步并更改/删除隔离级别。如何使方法同步?

@Singleton
@Path("/forgottenPath")
public class RestEndpoint {

@Inject
private oneService oneService;

@POST
@Path("/someAction")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public SomeResponse makeSomeAction() {
...
oneService.makeSomeAction();
...
}
}

public class OneServiceImpl implements OneService {

@Inject
private AnotherService anotherService;

@Override
public SomeRespose makeSomeAction() {
...
anotherService.getToken());
....
}
}

@Singleton
public class AnotherServiceImpl implements AnotherService {

@Override
@Transactional(isolation = Isolation.SERIALIZABLE)
public synchronized Token getToken() {
// modifies and retrieves info from database
}
}

最佳答案

这不是关于synchronized无法正常工作,而是关于@Transactional是如何实现的。

长话短说:Spring 不是直接调用事务方法(在您的例子中是 getToken()),而是创建代理类,用这样的方法替换所有事务方法(非常简化):

// Generated proxy class (either via AOP, dynamic proxy or bytecode generation)
@Override
public Token getToken() {
try {
transactionManager.startTransaction(params);
// Only this call is synchronized
return super.getToken();
}
catch (Throwable e) {
transactionManager.rollback();
rethrow();
}
finally {
// Not in synchronized method (lock is not held), but changes are not commited yet
transactionManager.commit();
transactionManager.closeTransaction();
}
}

参见this answer了解更多详情。

正如你所看到的,首先事务被打开,然后才调用你原来的getToken(),所以实际上,当你尝试获取锁时(进入synchronized方法),事务是已经创建。此外,当调用者退出时,您的 getToken() 方法锁被释放(从同步方法退出),但事务尚未提交。所以可能的比赛就在这里:

假设第一个线程打开事务,持有锁定,对数据库执行操作,退出原始方法,释​​放锁定,然后暂停一下。然后第二个线程可以执行相同的操作,第一个线程被唤醒并且它们都尝试提交,因此其中一个事务应该失败。

回答您原来的问题,为了避免更改隔离级别并允许序列化访问,您需要不在服务中同步,而是在服务周围同步。

三种解决方案:

1)使调用者方法同步(在您的情况下为makeSomeAction)

2) 如果您不希望整个方法同步,请为其创建锁:

@Override
public SomeRespose makeSomeAction() {
...
// Instance of ReentrantLock
lock.lock();
try {
anotherService.getToken());
}
finally {
lock.unlock();
}
....
}

3)如果要封装同步逻辑,则创建阻塞适配器:

@Singleton
public class AnotherServiceAdapter implements AnotherService {

@Autowired
private AnotherServiceImpl originalService;

@Override // No transactional here => no aop proxy
public synchronized Token getToken() {
// Lock is held before transactional code kicks in
return originalService.getToken();
}
}

关于Java的synchronized方法并不是同步的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37070162/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com