我正在阅读一本关于Java的书,有一个这个练习题,他们用一个私有变量声明了一个类,一个公共void
方法做了一些昂贵的操作来计算,然后设置私有变量,第二个返回私有变量的公共方法。问题是“如何使这个线程安全”,一个可能的答案是“同步两个方法中的每一个”,另一个可能的答案是“这个类不能成为线程安全的”。
我认为类不能成为线程安全的,因为即使你同步这两个方法,你可能会遇到Thread1调用setter的情况,在Thread1调用getter之前,Thread2可能会执行并调用setter,所以当Thread1去了并检索结果时,它会得到错误的信息。这是看待事物的正确方法吗?书中提出的正确答案是,通过同步这两种方法可以使类成为线程安全,现在我很困惑......
答案 0 :(得分:3)
我认为类不能成为线程安全的,因为即使你同步这两个方法,你可能会遇到Thread1调用setter的情况,在Thread1调用getter之前,Thread2可能会执行并调用setter,所以当Thread1去了并检索结果时,它会得到错误的信息。这是看待事物的正确方法吗?
你是对的。没有办法保证线程不会在每个方法的调用之间调用任何一种方法,来自类。
如果你想这样做,那就需要一个包装类。 所以如果带有getter和setter的类是这样的:
class Foo
{
private static int bar;
public static synchronized void SetBar(int z) { ... }
public static synchronized int GetBar() { ... }
}
包装类看起来像这样:
class FooWrapper
{
public synchronized int SetGetBar(int z)
{
Foo.SetBar(z);
return Foo.GetBar();
}
}
保证这一点的唯一方法是,如果你可以保证所有的调用都将通过你的包装类,而不是直接通过类Foo。
答案 1 :(得分:1)
当你使这两个同步时,getter和setter本身是线程安全的。更具体地说:
但是,使getter和setter 本身线程安全并不意味着应用程序作为一个整体(即使用此类的任何东西)是线程安全的。如果您的线程想要调用setter,那么在调用getter时获取相同的值,这涉及在不同级别上的同步。
就线程安全而言,线程安全类不需要控制如何调用其方法(例如,它不需要控制线程交换其调用的方式),但是它需要确保当时,方法会按照预期执行。
答案 2 :(得分:0)
synchronized
是一个对象范围的锁。任何给定对象中只能有一个synchronized
方法一次只能在任何给定的线程上执行。让我们来上课:
class Foo
{
private int bar;
public synchronized void SetBar() { ... }
public synchronized int GetBar() { ... }
}
SetBar()
。线程1获取对象锁。SetBar()
,但是线程1保持锁定。线程2现在排队等待线程1释放它时获取锁定。SetBar()
并释放锁定。SetBar()
。GetBar()
。现在,线程1排队等待线程2将释放它时获取锁定。SetBar()
并释放锁定。GetBar()
,并完成锁定。你做了两次工作,但没有造成任何竞争条件。两次工作可能会或可能不会是错误的,具体取决于它是什么。
一个常见的模式是让一个线程产生内容,另一个线程做一些有用的东西。这被称为生产者 - 消费者模式。在这种情况下,对于SetBar()
尝试GetBar()
以及尝试{{1}}的内容的人或内容没有混淆。