我想问与Java同步相关的问题,并澄清我的疑问,我编写了以下简单代码:
class A {
public int variable;
public int secondVariable;
public Object variableLock = new Object();
public Object secondVariableLock = new Object();
public void doingSthWithVariable() {
synchronized (variableLock) {
.... } }
public void doingSthWithVariableInOtherMethod() {
synchronized (variableLock) {
.... } }
public void doingSthWithSecondVariable() {
synchronized (secondVariableLock) {
.... } }
public void doingSthWithSecondVariableInOtherMethod() {
synchronized (secondVariableLock) {
.... } }
}
class B {
public A instanceOfA;
public void doingSthWithAVariables() {
synchronized (instanceofA.variableLock) {
synchronized (instanceofA.secondVariableLock) {
....} } }
}
我的问题是:是否安全,并且使用B类中A类的variableLock / secondVariableLock是一种好习惯吗? 我的意思是,我需要在类B的实例中阻止这两个变量的任何更改,并且我想知道这是否是一个好方法。 另一个问题:如果我在多个B对象中具有相同的instanceOfA,该怎么办?
在我看来,答案是肯定的(这不是不安全的),但我只是想确保并提出更好的解决方法。
答案 0 :(得分:0)
更常见的方法是实例化一个ReadWriteLock并从中产生两个写锁,并在A和B中使用它们。而A的相同实例将共享相同的锁。
答案 1 :(得分:0)
应该重新设计整个结构。
一个类应该尽可能多地封装其内部。在理想的世界中,没有其他班级应该看到这些字段,也不必知道何时/什么同步/锁定。一个类应该公开有意义的方法,这些方法可以回答有关此实例的问题或将该实例的状态从一个有效状态更改为另一个有效状态。
“有效状态”的概念取决于域,但通常涉及字段值的组合,例如(简化的)路口不应让所有四个交通信号灯都为绿色。如果这种情况暂时发生在状态更改方法内部,则外部环境一定不能看到这种情况。
使用synchronized
关键字是一种实现此目的的(传统)方法。您将声明synchronized
状态转换器以及所有可能受无效状态影响的方法。
如果状态分解成多个独立的部分,则可以仅对一个实例使用多个锁定对象,这些部分可以更改而不会互相影响。但是,然后,如果字段组是如此松散地耦合在一起,为什么要将此模型建模为一个大实例,而不是一堆较小的实例?
还有一点:如果允许其他类修改对实例的有效性至关重要的字段,则会完全破坏任何封装。然后,您不再进行面向对象的编程,而是将实例视为类似于1970年代和1980年代的PASCAL中的结构。
看看Java库中的线程安全类,例如java.util.Vector
。此类遵循简单的模式,只需声明一些基本方法synchronized
,即使用实例本身作为锁定对象,即可一次仅允许一个线程访问关键代码。