如何避免在非最终字段上进行同步? [英] How to avoid synchronization on a non-final field?

查看:132
本文介绍了如何避免在非最终字段上进行同步?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果我们有2个在不同线程下对同一对象进行操作的类,并且希望避免出现竞争情况,则必须使用具有相同监视器的同步块,如下例所示:

If we have 2 classes that operate on the same object under different threads and we want to avoid race conditions, we'll have to use synchronized blocks with the same monitor like in the example below:

class A {
    private DataObject mData; // will be used as monitor

    // thread 3
    public setObject(DataObject object) {
       mData = object;
    }

    // thread 1
    void operateOnData() {
        synchronized(mData) {
            mData.doSomething();
            .....
            mData.doSomethingElse();
        }
    }
}

  class B {
    private DataObject mData;  // will be used as monitor

    // thread 3
    public setObject(DataObject object) {
       mData = object;
    }

    // thread 2
    void processData() {
        synchronized(mData) {
            mData.foo();
            ....
            mData.bar();
        }
    }
}

我们将要操作的对象将通过调用setObject()进行设置,之后将不变.我们将使用该对象作为监视器.但是,智能将警告非最终字段上的同步.

The object we'll operate on, will be set by calling setObject() and it will not change afterwards. We'll use the object as a monitor. However, intelliJ will warn about synchronization on a non-final field.

在这种特殊情况下,非本地字段是可以接受的解决方案吗?

In this particular scenario, is the non-local field an acceptable solution?

上述方法的另一个问题是,不能保证在线程3设置之后,线程1或线程2将监视该监视器(mData),因为设置和读取监视器之间尚未建立之前"关系.例如,线程1仍可将其视为null.我的猜测正确吗?

Another problem with the above approach is that it is not guaranteed that the monitor (mData) will be observed by thread 1 or thread 2 after it is set by thread 3, because a "happens-before" relationship hasn't been established between setting and reading the monitor. It could be still observed as null by thread 1 for example. Is my speculation correct?

关于可能的解决方案,使DataObject线程安全不是一个选择.在类的构造函数中设置监视器并将其声明为final即可.

Regarding possible solutions, making the DataObject thread-safe is not an option. Setting the monitor in the constructor of the classes and declaring it final can work.

编辑从语义上讲,所需的互斥与DataObject有关.这就是我不想拥有辅助显示器的原因.一种解决方案是在DataObject上添加lock()unlock()方法,在进行处理之前需要先调用它们.在内部,他们将使用Lock对象.因此,operateOnData()方法变为:

EDIT Semantically, the mutual exclusion needed is related to the DataObject. This is the reason that I don't want to have a secondary monitor. One solution would be to add lock() and unlock() methods on DataObject that need to be called before working on it. Internally they would use a Lock Object. So, the operateOnData() method becomes:

 void operateOnData() {
     mData.lock()
     mData.doSomething();
     .....
     mData.doSomethingElse();
     mData.unlock();
 }

推荐答案

您可以创建一个包装器

class Wrapper
{
    DataObject mData;

    synchronized public setObject(DataObject mData)
    {
        if(this.mData!=null) throw ..."already set"
        this.mData = mData;
    }

    synchronized public void doSomething()
    {
        if(mData==null) throw ..."not set"

        mData.doSomething();
    }

创建一个包装器对象,并将其传递给A和B

A wrapper object is created and passed to A and B

class A 
{
    private Wrapper wrapper; // set by constructor

    // thread 1
    operateOnData() 
    {
        wrapper.doSomething();
    }

线程3也有对包装器的引用;可用时会调用setObject().

Thread 3 also has a reference to the wrapper; it calls setObject() when it's available.

这篇关于如何避免在非最终字段上进行同步?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆