1

我发现我经常做以下类似的事情。制作对象的副本以发送到线程。线程是唯一使用过该对象的线程,并且我们有一个happens-before关系,所以它是线程安全的。

但这让我感到紧张。正如评论所述,如果有人出现并欺骗 objForThread 怎么办?我应该使用锁吗?或者这是一个普遍接受的java模式?

class Example
{
  private SomeObj mDynamicObj = new SomeObj();

  public void doWorkInAThread()
  {
    mutateThis(mDynamicObj);
    final SomeObj objForThread = new SomeObj(mDynamicObj);

    myExecutorService.submit(new Runnable() { @Override public void run()
    {
      doSomethingWith(objForThread);
    }});

    mutateThis(mDynamicObj);

    // Concerned that in the future someone will come
    // along and mutate objForThread here making this thread unsafe
  }
}
4

3 回答 3

1

如果您感到紧张,最好将引用传递给线程,而不是将其保留在本地:

class Example
{
    private SomeObj mDynamicObj = new SomeObj ();

    public void doWorkInAThread ()
    {
        class MyRunnable implements Runnable
        {
            private final SomeObj objForThread;

            public MyRunnable (SomeObj objForThread)
            {
                this.objForThread = objForThread;
            }

            @Override
            public void run ()
            {
                doSomethingWith (objForThread);
            }
        }

        mutateThis (mDynamicObj);

        myExecutorService.submit (new MyRunnable (new SomeObj (mDynamicObj)));

        mutateThis (mDynamicObj);
    }
}
于 2013-02-08T23:03:35.397 回答
0

保护您的代码免受人们做出可能破坏它的更改是很困难的。

你可以争辩说这不是你的问题。您可以添加警告评论并继续。或者你可以假设下一个人足够聪明/小心,可以弄清楚他的变化的含义。

或者您可以采取防御措施,例如将 SomeObj 更改为线程安全甚至不可变的……即使这可能会增加额外的运行时开销。

哪种方法最好?我不认为我可以就此提出建议。这取决于更高级别的问题;例如,团队及其代码审查和测试程序有多​​好,整个应用程序有多复杂,性能有多重要,错误有多重要,等等。


对于这个特定的“示例”,您已经抽象出任何类似于应用程序逻辑的东西,因此很难知道哪种方法是最好的。传递本地对象是“好”还是“坏”取决于上下文。

于 2013-02-09T02:51:03.280 回答
0

这完全取决于您要达到的目标。一些数据被设计成可以被多个线程访问和修改。某些数据设计为仅在线程安全环境中使用。

如果你需要更详细的解释,你必须提供一个真实的例子。

此外,某些类被设计为线程安全或不可变的。例如,共享 String 或 Integer 是非常好的。但是,对这些对象的实际引用可能会发生变化,因此如果您依赖这样的引用,那么您可能会遇到问题。

在您的示例中,后者并非如此,因为您通过最终变量引用对象。但是,如果您要引用成员变量 mDynamicObject,那么如果另一个线程中的某个人为其分配了不同的对象(mDynamicObject = new SomeObj()),您就会遇到问题。如果没有适当的同步,它可能会导致您的应用程序出现奇怪的状态。为避免这种情况,您可以将其分配给最终变量和引用。

考虑尽可能多地将参数作为参数传递给新线程调用,而不是让它们被引用。这将保证您引用的内容不会被更改。

当然,如果需要,对象本身最好是不可变的或适当同步的。

于 2013-02-08T22:59:30.757 回答