现在我正在尝试更多地了解java线程,我有一个小问题,我找不到任何地方的直接答案。假设我有两个共享对象的线程:
public class FooA implements Runnable
{
Object data;
public FooA(final Object newData)
{
data = newData;
}
public void doSomething()
{
synchronized(data)
{
data = new Integer(1);
}
}
public void run() {
// Does stuff
}
}
public class FooB implements Runnable
{
Object data;
public FooB(final Object newData)
{
data = newData;
}
public void doSomething()
{
synchronized(data)
{
System.out.println(data);
}
}
}
FooA会阻止FooB进入代码的doSomething部分吗?或相反亦然?我的直觉是肯定的,但根据我正在阅读的书,它说不。因此需要监控对象。我做了一个稍微复杂一点的版本,一切正常。
我环顾四周,但找不到具体的答案。
答案 0 :(得分:3)
此示例存在一些问题。
首先,synchronized(data)
表示它同时在data
中的对象上进行同步。如果您使用相同的对象初始化了两个对象,则应该进行同步。
但是,由于您在代码中设置了data
,因此在此之后实际上不会起作用(因为它不会是同一个对象)。
final
不是特别有用。作为场上的修饰语本身会更有用。它在这个特定的例子中不起作用,因为你正在修改这个值,但一般来说,当你知道要修复的值时,它是防止一些并发问题的好方法。
我做了一个稍微复杂的版本 这一切,一切正常。
通过反复试验来调试并发问题是非常困难的,或者几乎是不可能的。它没有失败的事实并不意味着它能够可靠地运作。
答案 1 :(得分:2)
问题是其中一个同步块会将新对象分配给data
。如果该块首先启动,并且更改data
,则后续运行将使用另一个对象来锁定。所以从那以后,两者都可以同时运行。
答案 2 :(得分:1)
在这种情况下答案是肯定的,但它很脆弱(即代码的下一次更改可能会破坏某些东西)。它为什么有效?
因为FooB
从未注意到FooA
更改了对象(每个线程都有自己的引用,所以FooB
在FooA
为其引用分配新值时从不会注意到这一点。
对于这样的情况,我建议使用AtomicReference
来确保两个线程可以访问同一个对象,任何人都可以随时更新该引用,其他线程只在更新后获取新值。 / p>
答案 3 :(得分:1)
任何Java对象都可用于同步。
如果FooA和FooB都是使用对同一对象的引用构造的,那么它们共享相同的“锁定”对象并将按预期阻塞。作为
Object data;
decalrations不是最终的,无论是FooA还是FooB,或者两者都可以为数据分配不同的值,然后在不同的对象上进行同步 - 这可能是好的还是坏的,这取决于你想要做什么。
答案 4 :(得分:1)
在您的代码示例中,fooA.data
和fooB.data
不是同一个对象,除非有人使用以下内容对其进行初始化:
Object o = new Object();
FooA fooA = new FooA(o);
FooB fooB = new FooB(o);
除非它们被初始化为同一个实例,否则它们不是同一个对象,只是相同的类型和名称。
当FooA
将new Integer(1)
分配给data
时,它们将不再是同一个对象,只是相同的类型和名称。因此,除非您致电:
fooB.data = fooA.data;
这必须在同步块中发生以保证同步执行。
另外,关于线程的一些事情是,即使一切都工作一次,这并不意味着你的程序是正确的,或者每次都能正常工作。线程问题只发生在正确的时间恰好(或者只是错误的情况下)。
答案 5 :(得分:0)
只要您必须在同一个对象上进行同步,这是正确的,但由于一个线程修改了“data”引用的对象,因此您需要同步“this”。