我应该如何正确阻止从其他线程访问此对象?

时间:2013-11-17 20:32:56

标签: java multithreading concurrency

我正在做这个小练习,试图理解我应该如何使用并发和线程。

有时候我有一个对象无法修改其源代码并且不是线程安全的,所以我希望它只能由一个线程访问。

在此示例中,我无法触摸的第三方对象称为Holdeable。我所做的是尝试将其包装到一个名为Holder的类中,该类具有同步方法,我希望通过这样做,只有一个线程可以访问该Holdeable对象。在某些时候,我取消了对Holdeable对象的引用,我希望它正确完成,所以当另一个线程计算mHolder.getHoldeable()== null为true时,并避免输入可能导致NullPointerException的代码。

我的最后一次尝试包括一个synchronized块,即:

class Holder {
    Holdeable mHoldeable;
    public synchronized void setHoldeable(Holdeable holdeable) { mHoldeable = holdeable; }
    public synchronized Holdeable getHoldeable() { return mHoldeable; }

}

class Holdeable { // Cannot be modified, that would be to cheat :D
    public int someValue;

}
public class MainClass {
    private static Holder mHolder;


    public static void main(String[] args) {
        try {
            Holdeable holdeable = new Holdeable();
            mHolder = new Holder();
            mHolder.setHoldeable(holdeable);

            new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        while(true) {   
                            synchronized(mHolder) {
                                if(mHolder.getHoldeable() != null) {
                                    Thread.sleep(23);
                                    System.out.println(mHolder.getHoldeable().someValue);
                                } else {
                                    System.out.println("No holder!"); 
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            Thread.sleep(1000);
            mHolder.getHoldeable().someValue = 2;
            Thread.sleep(1500);
            mHolder.getHoldeable().someValue = 3;
            Thread.sleep(500);
            mHolder.setHoldeable(null);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }



} 

这个例子避免了抛出NullPointerException,但正如你所看到的,执行这么多锁定需要花费很多。我在阅读“并发艺术”一书的时候正在努力解决这个问题,看看我是否最终得到它。

您怎么看?

2 个答案:

答案 0 :(得分:2)

您的代码不是线程安全的。您有两个级别的锁定:

  1. 持有人的方法是同步的;
  2. 您在Holder实例上进行同步。
  3. 第1点没有给你足够的同步,因为它只涵盖了抓取 holdeable而没有访问其属性;

    第2点根本没有给你任何同步,因为你只在一个线程中获取锁。

    我建议你只使用第2点并一致地应用它。

    BTW你的程序执行时间太长,因为它调用了Thread.sleep。锁定的性能太高,你无法在不涉及重复循环至少数十万次的情况下注意到它。

答案 1 :(得分:0)

public class Holder {
  final Holdeable mHoldeable;

  Holder(Holdeable holdeable) {
    this.mHoldeable = Objects.requireNonNull(holdeable, "Holdeable cannot be null");
  }

  Holdeable get() {
    return mHoldeable;
  }
}

使用这种结构将使您的生活变得更加轻松。正如您可能已经注意到的那样:它也消除了同步的需要。如果我写过一本书,那就在第一页上。 ;)

线程和同步几乎100%在架构级别上完成,添加一些同步块只是一个备份/快速&肮脏的解决方案。