1

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

我认为无法使该类成为线程安全的,因为即使您同步这两种方法,您也可能会遇到 Thread1 会调用 setter 并且在 Thread1 可以调用 getter 之前,Thread2 可能会执行并调用 setter,因此当 Thread1去检索结果它会得到错误的信息。这是看待事物的正确方式吗?这本书建议正确的答案是通过同步这两种方法可以使类成为线程安全的,现在我很困惑......

4

3 回答 3

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。

于 2013-08-23T02:19:43.347 回答
1

当您使这两个同步时,getter 和 setter 本身是线程安全的。进一步来说:

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

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

    就线程安全而言,线程安全类不需要控制方法的调用方式(例如,它不需要控制线程交错调用的方式),但它需要确保它们被调用时,方法做他们应该做的。

  • 于 2013-08-23T01:35:55.957 回答
    0

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

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

    你做了两次工作,但你没有引起任何竞争条件。做两次工作可能是错误的,也可能不是错误的,这取决于它是什么。

    一种常见的模式是让一个线程产生内容,而另一个线程用它做一些有用的事情。这称为生产者-消费者模式。在这种情况下,不会混淆谁或什么尝试SetBar()以及什么尝试GetBar()

    于 2013-08-23T01:36:49.107 回答