Java同步方法未同步

Justinas Jakavonis

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

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
    }
}
qwwdfsad

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

长话短说:getToken()Spring创建了代理类,而不是直接调用事务方法(在您的情况下),该代理类将所有事务方法替换为以下内容(非常简化):

// 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();
    }
}

有关更多详细信息,请参见此答案

如您所见,首先打开事务,然后才getToken()调用原始事务,因此实际上,当您尝试获取锁(输入同步方法)时,已经创建了事务而且,当调用者退出时,您的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();
    }
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

Java同步方法未同步

来自分类Dev

由于未同步的同步方法,导致ConcurrentModicationException

来自分类Dev

AVPlayer未同步

来自分类Dev

代码未同步运行

来自分类Dev

MergeContent未同步NIFI

来自分类Dev

如果Singleton getInstance()方法未同步,Junit测试将失败

来自分类Dev

iCloud Core数据未同步

来自分类Dev

Spring可缓存未同步

来自分类Dev

画布鼠标指针未同步

来自分类Dev

MariaDB Galera群集未同步

来自分类Dev

嵌套存储操作未同步

来自分类Dev

信号量C#对象同步方法是从未同步的代码块中调用的

来自分类Dev

Java同步方法调用非同步方法

来自分类Dev

Java:同步方法如何与静态同步方法同步?

来自分类Dev

Java线程同步方法

来自分类Dev

Java方法上的同步

来自分类Dev

Java同步方法-OCPJP

来自分类Dev

Java同步方法使用

来自分类Dev

Java中的同步方法和同步块

来自分类Dev

函数未同步返回值

来自分类Dev

大写锁定指示灯未同步

来自分类Dev

大写锁定指示灯未同步

来自分类Dev

运行ntpd但未同步的VM

来自分类Dev

Windows时间未同步-“访问被拒绝”错误

来自分类Dev

在Linux VM中时间未同步

来自分类Dev

内核恐慌-未同步:找不到init

来自分类Dev

OneNote笔记本的位置(未同步)

来自分类Dev

ynchronizationLockException对象未同步-分析登录问题

来自分类Dev

为什么未同步的对象比同步的对象性能更好?