将getter与输入处理方法分开的类可以被认为是“线程安全的”吗?

时间:2013-08-23 01:30:13

标签: java concurrency thread-safety

我正在阅读一本关于Java的书,有一个这个练习题,他们用一个私有变量声明了一个类,一个公共void方法做了一些昂贵的操作来计算,然后设置私有变量,第二个返回私有变量的公共方法。问题是“如何使这个线程安全”,一个可能的答案是“同步两个方法中的每一个”,另一个可能的答案是“这个类不能成为线程安全的”。

我认为类不能成为线程安全的,因为即使你同步这两个方法,你可能会遇到Thread1调用setter的情况,在Thread1调用getter之前,Thread2可能会执行并调用setter,所以当Thread1去了并检索结果时,它会得到错误的信息。这是看待事物的正确方法吗?书中提出的正确答案是,通过同步这两种方法可以使类成为线程安全,现在我很困惑......

3 个答案:

答案 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本身是线程安全的。更具体地说:

  • 调用setter时,可以保证变量的值是方法完成时设置的值。
  • 当您调用getter时,可以保证返回值是您进行调用时变量的值。

    但是,使getter和setter 本身线程安全并不意味着应用程序作为一个整体(即使用此类的任何东西)是线程安全的。如果您的线程想要调用setter,那么在调用getter时获取相同的值,这涉及在不同级别上的同步。

    就线程安全而言,线程安全类不需要控制如何调用其方法(例如,它不需要控制线程交换其调用的方式),但是它需要确保时,方法会按照预期执行。

  • 答案 2 :(得分:0)

    Java中的

    synchronized是一个对象范围的锁。任何给定对象中只能有一个synchronized方法一次只能在任何给定的线程上执行。让我们来上课:

    class Foo
    {
        private int bar;
    
        public synchronized void SetBar() { ... }
        public synchronized int GetBar() { ... }
    }
    
    • 主题1调用SetBar()。线程1获取对象锁。
    • 线程2想要调用SetBar(),但是线程1保持锁定。线程2现在排队等待线程1释放它时获取锁定。
    • 线程1完成执行SetBar()并释放锁定。
    • 线程2立即获取锁定并开始执行SetBar()
    • 主题1调用GetBar()。现在,线程1排队等待线程2将释放它时获取锁定。
    • 线程2完成执行SetBar()并释放锁定。
    • 线程1获取锁定,执行GetBar(),并完成锁定。

    你做了两次工作,但没有造成任何竞争条件。两次工作可能会或可能不会是错误的,具体取决于它是什么。

    一个常见的模式是让一个线程产生内容,另一个线程做一些有用的东西。这被称为生产者 - 消费者模式。在这种情况下,对于SetBar()尝试GetBar()以及尝试{{1}}的内容的人或内容没有混淆。