2013-07-26 99 views
14

我想知道一个类的每个实例是否拥有该类中方法的副本?实例变量的实例方法和线程安全

比方说,我有以下类MyClass

public MyClass { 

    private String s1; 

    private String s2; 

    private String method1(String s1){ 
    ... 
    } 

    private String method2(String s2){ 
    ... 
    } 
} 

因此,如果两个型动物的用户作出一个实例的MyClass,如:

MyClass instanceOfUser1 = new MyClass(); 
MyClass instanceOfUser2 = new MyClass(); 

是否知道每个用户都在自己的线程副本MyClass的方法?如果是,那么实例变量就是线程安全的,只要实例方法操纵它们,对吧?

我在问这个问题,因为我经常读到实例变量不是线程安全的。我不明白为什么它应该是这样的,每个用户通过调用new运算符得到一个实例?

+0

你问每个线程有不同的方法为每个线程有自己的MyClass的实例或MyClass的一个实例吗? – Jeffrey

+0

没有实例变量本质上是线程安全的。如果两个线程可以修改对同一个对象的引用,那么你需要实现某种同步 – Joel

+6

你真的很困惑。线程安全与状态有关。国家是关于领域,而不是方法。你的类的线程安全性完全取决于方法如何使用字段s1和s2。没有他们的身体使得这个问题无法回答。 –

回答

9

每个对象都获得自己的类实例变量副本 - 它是static在类的所有实例之间共享的变量。实例变量不一定是线程安全的原因是它们可能会被多个调用非同步实例方法的线程同时修改。

class Example { 
    private int instanceVariable = 0; 

    public void increment() { 
     instanceVariable++; 
    } 
} 

现在,如果两个不同的线程在同一那么你已经有了一个数据叫赛increment - instanceVariable可能有1或2的两种方法返回的结束递增。您可以通过将​​关键字添加到increment或使用AtomicInteger而不是int等来消除此数据竞争,但重点在于,仅仅因为每个对象都获取其类实例变量的副本并不一定意味着变量以线程安全的方式访问 - 这取决于类的方法。 (例外是final不可变变量,这些变量不能以线程不安全的方式访问,缺少类似于序列化黑客的东西。)

+2

等待,所以如果在每个线程中使用了一个全新的实例,那么您说'instanceVariable'字段对于冲突读/写不安全?这*是OP正在谈论的情景,不是? –

2

在许多情况下,可以从多个类访问实例。例如,如果你的实例是另一个类中的一个静态变量,那么所有的线程都会共享这个实例,这样你就会遇到很大的麻烦。这只是我脑海中浮现出来的第一种方式......

5

多线程问题主要出现在静态变量和类的实例被同时访问。

你不应该担心类中的方法,而应该更多地关注字段(意味着在类级别上的作用域)。如果存在多个对类实例的引用,则不同的执行路径可能会尝试同时访问该实例,从而导致意外的后果,例如竞争条件。

一个类基本上是一个制作对象实例的蓝图。当对象被实例化时,它会在内存中接收一个被引用访问的点。如果多于一个的线程具有该引用的句柄,则它可能会导致实例同时访问的情况,这将导致字段被两个线程操纵。

4

“实例变量不是线程安全的” - 此语句取决于上下文。 确实如此,例如你正在谈论Servlets。这是因为,Servlet只创建一个实例,多个线程访问它。所以在这种情况下,实例变量不是线程安全的。

在上面的简化情况下,如果您为每个线程创建新实例,那么您的实例变量是线程安全的。

希望这回答你的问题

2

每个实例都有自己的一组实例变量。你会如何检测每个实例是否有不同的方法副本?只有通过检查实例变量的状态才能看出差异吗?

实际上,不是,只有一个方法副本,意思是调用该方法时执行的一组指令。但是,执行时,实例方法可以使用保留的标识符this引用它正在调用的实例。 this标识符引用当前实例。如果您没有使用其他方法限定实例变量(或方法),则暗示为this

例如,

这里只有一个弥补setFlag()setAnotherFlag()方法VM指令字节的副本。但是当它们被调用时,this被设置为调用发生的实例。因为this对于非限定变量是隐含的,所以可以在示例中删除对this的所有引用,并且它仍然会完全相同。

但是,如果变量是合格的,如上面的friend.flag,则可以引用另一个实例的变量。这是如何在多线程程序中遇到问题的。但是,只要一个对象不会从另一个线程“逃脱”出来以便其他人看到,就没有什么可担心的。

4

A method不过是一组指令。无论哪个线程调用方法,请获取这些说明的副本。之后开始执行。该方法可以使用局部变量method and thread-scoped,或者可以使用共享资源,如静态资源,共享对象或其他资源,这些资源是visible across threads