确保只有一个线程在一个类

时间:2016-08-23 09:24:47

标签: java multithreading concurrency

我有一个DAO类,它有多个更新数据库表的方法。这些方法在整个代码库中以许多不同的顺序在许多不同的地方调用。

public class Dao {
  public synchronized updateA()
  public synchronized updateB()
  public synchronized updateC()
  public synchronized getA()
  public synchronized getB()
  public synchronized getC()
}

我遇到问题,Class1希望按顺序呼叫getA()getB()getC()。三个表A,B和C是相关的,所以我需要在一个时间点使它们的状态同步。

但是,在Class1调用getA()之后,在调用getB()之前,另一个线程上的Class2跳入并调用updateB(),这会破坏事情。

当有任何线程在那里时,是否可以锁定整个DAO类,并且只有在它完成后解锁它?

我已经尝试在DAO类中放置一个静态ReentrantLock并将其从Class1锁定,但我不确定如何从那里开始。

我已经达到了以下目标:

public class Class1 {
  public void check() {
    dao.daoLock.lock();
    dao.getA();
    dao.getB();
    dao.getC();
    dao.daoLock.unlock();
  }
}

public class Dao {
  public static final daoLock = new ReentrantLock();
  public synchronized updateA() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized updateB() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized updateC() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized getA() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized getB() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized getC() {
    daoLock.lock();
    // Do stuff...
  }
}

我不确定解锁的位置。如果我把它放在每个DAO类方法中,它会让其他线程不是吗?

这里有更好的解决方案吗?

2 个答案:

答案 0 :(得分:3)

您正尝试在客户端同步对数据库的访问,这从根本上说是错误的地方。如果另一个客户在此期间写了一个怎么办?

最好将此类同步保留在数据库本身,使用事务MySQLPostgreSQL)。

即使您没有多个客户端也永远不会,使用事务是一个更好的解决方案。否则,你的所有线程都会被阻塞,即使它们只是在进行读取,这原则上可以同时发生。

答案 1 :(得分:1)

除了托马斯的好答案之外,还有另外一个值得关注的方面: interfaces 应该以一种易于做正确事情的方式编写;并且很难做错事。

含义:如果某些操作要求你按顺序调用a(),b(),c(),并采用“整体锁定方式”,那么:而不是在你的界面上有a,b,c,你提供完全一个方法abc()...获取锁,调用a,b,c并释放锁。

所以,你应该退后一步,从SOLID的角度看一下你班级的界面;或者更具体 - 从事物的“单一责任原则”方面考虑它。

换句话说:拥有一个类,它提供了许多完全“分离”的方法;其中一些甚至需要“以非常特殊的方式一起使用”,这是一种清晰的设计气味;所以另一个迹象表明你最好退一步看看这个设计。