Java线程安全:当您必须调用实例变量上的方法时,如何处理实例变量



因此,在代码的线程安全性方面,我总体上做得很好,但我目前遇到的情况是,我不清楚处理它的最佳方法。

我有一个实例变量,非最终变量,因为封闭类可以更改其值。这个变量是一个对象,它有几个方法在应用程序中被调用。这里有一个简单的例子来说明我的意思。

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()方法时呢?由于存在死锁风险,我总是犹豫是否要在异类方法上同步。

在实际情况下,这是基于,有许多不同的变化。我公司的应用程序是一个庞大的代码库,经常像一碗意大利面条,所以当涉及到编写任何类型的同步代码时,我都会格外偏执,因为这种紧密的耦合,而且不仅在美国,而且在东欧的一家离岸公司都有开发人员在开发它,我不相信他们都能做出好的编码决策。

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

fooFoo.doSomething()//我如何确保这条线的安全?

提示:除非这是整个程序中唯一一个访问对象的行,否则无法确保单行线程的安全。

线程安全不是使特定的代码行或特定的方法线程安全:而是使数据线程安全。

fooFoo是否引用可变对象?如果不是,那么该行已经是线程安全的。但是,如果对象是可变的,那么线程安全至少意味着确保两个或多个线程之间的意外交互不会使该对象处于无效状态;在最坏的情况下,这意味着确保fooFoo对象和程序中其他对象之间关系的一致性。

任何时候,线程之间共享的两条或多条数据之间存在重要关系,那么您可能需要在可能暂时违反该关系的任何代码位周围抛出synchronized块,并且您需要在依赖于该关系的任意代码位附近抛出synchronized块——即使代码仅查看数据。

在您的情况下,您也必须将doSomething()设置为synchronized,因为每次对类的可变部分进行并发访问时都需要锁定。当您只在doSomething中读取fooFoo时,您可以同时在setFooFoo()中写入fooFoo,从而创建数据竞赛。synchronized本质上导致函数调用获取一个与入口处的Java对象相关联的锁,并在离开函数后释放它。

或者,您也可以在Foo中使用Lock成员,在执行任一操作时都可以使用该成员。这适用于您可能有多个独立成员的情况,这些成员可以在另一个成员被更改时安全访问。在这种情况下,使用两个不同的锁可能会大大加快代码的速度。

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

对于死锁问题:您担心这一点是正确的,但在您首先确保对象线程安全之后,请将其视为一个单独的问题。问题是还有哪些锁被拿走了,不可能仅仅用你发布的内容来回答这个问题。在给定对象的情况下,同步对象的读/写本身不会死锁,因为一次只能有一个操作处于活动状态,并且它们不会从代码中显示的内容中获取任何其他锁。

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

如果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);
    }
}

最新更新