当一个对象需要同步时,IDE会抱怨它是否设置为非final(因为它的引用不是持久的):
private static Object myTable;
....
synchronized(myTable){ //IDE complains!
//access myTable here...
}
我们都知道如果持有锁的线程改变非最终对象的引用,IDE会抱怨阻止另一个线程进入受保护的块。
但是,当线程A保存同一个对象的锁时,同步对象的引用是否也可以被另一个线程B更改?
答案 0 :(得分:5)
但是,当线程A保存同一个对象的锁时,同步对象的引用是否也可以被另一个线程B更改?
如果你的意思是“另一个线程可以改变myTable
变量的值,那么答案就是”绝对“......假设有一个允许的代码路径。它是一个私有变量,所以你应该是能够找到所有可以改变价值的代码。
持有锁只会阻止另一个线程获取相同的锁。它对哪些代码本身可以访问哪些变量没有任何影响。
作为旁注,区分对象,变量和变量的值(它是一个引用,而不是一个对象)很重要。 。所以没有“最终对象”这样的东西 - 只有变量(以及类和方法)可以是最终的。同样地,没有“同步对象”这样的东西,你不能改变“对象的引用” - 你可以改变变量的值,使它成为对不同对象的引用。在思考这里发生的事情时,在脑海中明确区分这些区别可能会有所帮助。
答案 1 :(得分:1)
是的,它可以。所有非最终变量/字段都可以随时更改其对对象的值/引用,无论对象是在受保护块内还是外。因此,您始终需要将第三方锁与非最终字段结合使用:
private static Object myTable;
private final static Object LOCK;
....
synchronized(LOCK){
//access myTable here...
}
仅将字段设置为最终字段会阻止来自 或 锁定内的任何引用更改。可以说后者是一种错误的Java行为,并且应该通过调用synchronized来锁定引用。但这就是它需要的方式,否则代码将变得无法维护。
嗯,为此目的,至少需要一些像private guardable Object o;
这样的新领域。 ;)
修改强>
这是测试类中的一个测试用例(请注意,构造函数加载线程非常不可预测,因此等待(1000)......但这只是一个测试,通常你不应该在构造函数中启动线程):
private volatile boolean first = true;
private TestObject testObject;
private Thread thread;
public Test(){
testObject = new TestObject();
thread = new Thread(this);
thread.start();
try {
synchronized(this){
wait(1000);
}
} catch (InterruptedException ex) {}
first = false;
thread = new Thread(this);
thread.start();
}
public void run() {
System.out.println("First: "+testObject.toString());
if(!first){
testObject = new TestObject();
}
synchronized(testObject){
System.out.println("Second: "+testObject.toString()+" locked!");
try {
synchronized(this){
System.out.println("Thread "+thread+" waiting!");
wait();
}
} catch (InterruptedException ex) {}
}
}
public static void main(String[] args) {
Test test = new Test();
}
结果是:
First: model.TestObject@12b6651
Second: model.TestObject@12b6651 locked!
Thread Thread[Thread-0,5,main] waiting!
First: model.TestObject@12b6651
Second: model.TestObject@4a5ab2 locked!
Thread Thread[Thread-1,5,main] waiting!
您可以在第5行看到没有通过锁阻止参考更改。然后测试用例改为:
private volatile boolean first = true;
private TestObject testObject;
private Thread thread;
private final Object LOCK = new Object();
...
public void run() {
System.out.println("First: "+testObject.toString());
if(!first){
testObject = new TestObject();
}
synchronized(LOCK){
...
产生这个结果:
First: model.TestObject@150bd4d
Second: model.TestObject@150bd4d locked!
Thread Thread[Thread-0,5,main] waiting!
First: model.TestObject@150bd4d
这里第二个线程等待获取LOCK ,这正是我们想要的。