2016-05-13 69 views
2

因此,当涉及到代码的线程安全性时,我总体上非常好,但是我目前遇到了一种情况,米不清楚处理它的最佳方式。Java线程安全:当你需要调用一个实例变量时,如何处理一个实例变量

我有一个实例变量,非final,因为封闭类可以更改它的值。这个变量是一个对象,有几个方法在应用程序中被调用。这里有一个快速示例来展示我的意思。

private class Foo{ 

     private FooFoo fooFoo; 

     public synchronized void setFooFoo(FooFoo fooFoo){ 
      this.fooFoo = fooFoo; 
     } 

     public void doSomething(){ 
      fooFoo.doSomething(); //How do I make this line thread-safe? 
     } 

    } 

更改fooFoo字段的引用很容易,只是简单的同步。但是什么时候在fooFoo上调用doSomething()方法呢?由于死锁风险,我总是犹豫是否采用外来方法。

在实际情况中,这是基于此,有许多不同的变化。我的公司的应用程序是一个庞大的代码库,通常类似于一碗意大利面,所以当编写任何类型的同步代码时,我会变得更加偏执狂,因为这种紧密的耦合以及开发人员不仅在这里美国,但在东欧的离岸公司工作,我不相信他们都会做出好的编码决定。

所以我只是在多线程环境中寻找处理这种情况的最佳实践。谢谢。

+1

你为什么认为这是'死锁风险'? –

+0

为什么你认为doSomething()方法的简单同步当setFooFoo()足够时不起作用?如果你在两个方法上都放了synchronized关键字,那么这个锁就会在同一个对象上,因此不会有死锁的机会。 – Vijay

+1

给定三个线程,如果线程'A'和线程'B'都同时调用'setFooFoo',并且如果线程'C'调用'fooFoo.doSomething()'你想要发生什么,你会想要发生什么?那时候。即使没有同步化,这种情况也不会导致崩溃。 – OldCurmudgeon

回答

0

在你的情况下,你将不得不使doSomething()​​,因为你需要锁定每一个类的可变部分的并发访问发生。虽然您仅在doSomething中读取fooFoo,但您可以同时在setFooFoo()中编写fooFoo,从而创建数据竞赛。​​本质上会导致函数调用在入口处获取与Java对象关联的锁,并在您离开函数后释放它。

另外,您也可以在Foo内使用Lock成员。这适用于您可能有多个独立成员的情况,这些成员可以安全访问而另一个正在更改。在这种情况下,使用两个不同的锁可能会使您的代码更快。

为了完整起见,应该提到的是,在一些较旧的Java版本(我相信只有Java 5)通过​​方法获取对象的内部锁定时,要比使用锁定对象慢很多。

对于死锁问题:您担心这一点是正确的,但在首先让对象线程安全后,将其视为单独的问题。问题是有什么其他的锁被采取,这是不可能回答这与你发布的内容。考虑到你的对象,同步对象上的读/写操作本身不会造成死锁,因为当时只有一个操作可以处于活动状态,并且它们不会从你在代码中显示的内容中获取任何其他锁。

0

这取决于您对线程安全性的关注。

如果foo只委托给您,您可以简单地将其变为易失性。这将防止线程在更新引用时兑现对旧值的引用。然后FooFoo可以处理它自己的线程安全问题。

private class Foo{ 

    private volatile FooFoo fooFoo; 

    public void setFooFoo(FooFoo fooFoo){ 
     this.fooFoo = fooFoo; 
    } 

    public void doSomething(){ 
     fooFoo.doSomething(); 
    } 

} 

如果您关注的是关于Foo自己的线程安全,并且它做更多,那么你应该同步相关的方法只是委托调用。

private class Foo{ 

    private FooFoo fooFoo; 

    public synchronized void setFooFoo(FooFoo fooFoo){ 
     this.fooFoo = fooFoo; 
    } 

    public synchronized void doSomething(){ 
     fooFoo.doSomething(); 
    } 

    public synchronized void doSomethingElse() { 
     int values = fooFoo.getValue(); 
     // do some things 
     fooFoo.setValue(values + somethingElse); 
    } 
} 
+0

你忘了同步你的第二个样本中的其他方法。目前,后者甚至不提供必要的可视性保证。 – Voo

+0

好的,谢谢,我会更新。 – cyroxis

2

fooFoo.doSomething(); //我如何使这一行是线程安全的?

提示:除非是整个程序中唯一访问该对象的行,否则不能使该行成为线程安全的。

线程安全并不是要使特定的代码行或特定的方法线程安全:它是关于使线程安全的数据

fooFoo是否引用可变对象?如果不是,那么该线已经是线程安全的。但是如果对象可变,那么线程安全至少意味着确保两个或更多线程之间的意外交互无法将该对象置于无效状态;在最糟糕的情况下,这意味着要确保fooFoo对象与程序中的其他对象之间的关系一致。如果在线程之间共享两个或多个数据片段之间存在重要关系,那么您可能需要在可能暂时违反该关系的任何代码块周围放置一个​​块,并且您需要抛出一个​​块一个​​块围绕取决于的任何位代码 - 即使代码只有看起来的数据。

相关问题