实例变量的实例方法和线程安全性
我想知道一个类的每个实例是否都有自己的类中的方法副本?
可以说,我有以下课程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
运算符获取实例时,我不明白为什么它应该是这样的?
每个对象都有自己的类实例变量的副本 – 它是在类的所有实例之间共享的static
变量。 实例变量不一定是线程安全的原因是它们可能被多个调用非同步实例方法的线程同时修改。
class Example { private int instanceVariable = 0; public void increment() { instanceVariable++; } }
现在,如果两个不同的线程同时调用increment
,那么您将获得数据争用 – 在返回的两个方法结束时, instanceVariable
可能会增加1或2。 你可以通过添加synchronized
关键字来increment
,或者使用AtomicInteger
而不是int
等来消除这种数据竞争,但重点是因为每个对象获得它自己的类的实例变量的副本并不一定意味着变量以线程安全的方式访问 – 这取决于类的方法。 (例外是final
不可变变量,不能以线程不安全的方式访问,缺少像序列化黑客那样的傻瓜。)
multithreading的问题主要出现在静态变量和同时访问的类的实例中。
您不应该担心类中的方法,而应该更多关于字段(意味着在类级别作用域)。 如果存在对类实例的多个引用,则不同的执行路径可能会同时尝试访问该实例,从而导致意外后果,例如竞争条件。
类基本上是用于创建对象实例的蓝图。 当对象被实例化时,它接收内存中由引用访问的点。 如果多个线程具有此引用的句柄,则可能导致同时访问实例的情况,这将导致两个线程都操纵字段。
‘实例变量不是线程安全’ – 此语句取决于上下文。 确实,如果你在谈论Servlets。 这是因为,Servlets只创建一个实例,多个线程访问它。 因此,在这种情况下,Instance Variables不是线程安全的。
在上面简化的情况下,如果要为每个线程创建新实例,那么您的实例变量是线程安全的。
希望这能回答你的问题
method
只是一组指令。 无论哪个线程调用该方法,都要获取这些指令的副本。 之后执行开始。 该方法可以使用method and thread-scoped
局部变量,或者它可以使用共享资源,如静态资源,共享对象或visible across threads
其他资源。
在许多情况下,可以从多个类访问实例。 例如,如果您的实例是另一个类中的静态变量,那么所有线程都将共享该实例,并且您可以通过这种方式遇到大麻烦。 这只是第一种进入我脑海的方式……
每个实例都有自己的一组实例变量。 您如何检测每个实例是否都有方法的独特“副本”? 仅通过检查实例变量的状态,这些差异是否可见?
实际上,不,该方法只有一个副本,这意味着在调用该方法时执行的指令集。 但是,在执行时,实例方法可以引用使用保留标识符调用它的实例。 该标识符引用当前实例。 如果您没有使用其他内容限定实例变量(或方法),则暗示this
一点。
例如,
final class Example { private boolean flag; public void setFlag(boolean value) { this.flag = value; } public void setAnotherFlag(Example friend) { friend.flag = this.flag; } }
只有一个字节副本构成了setFlag()
和setAnotherFlag()
方法的VM指令。 但是当它们被调用时, this
被设置为发生调用的实例。 因为this
是对非限定变量的隐含,所以您可以在示例中删除this
所有引用,并且它仍将完全相同。
但是,如果变量是合格的,如上面的friend.flag
,则friend.flag
另一个实例的变量。 这就是你在multithreading程序中遇到麻烦的方法。 但是,只要一个对象没有从一个线程“逃脱”到其他人可见,就没有什么可担心的了。