简体   繁体   English

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

[英]Can a class with getters separate from input processing methods be considered “thread-safe”?

I was reading though a book on Java and there was this exercise question where they declared a class with one private variable, one public void method that did some expensive operation to calculate and then set the private variable, and a second public method to return the private variable. 我正在读一本关于Java的书,有一个练习题,他们用一个私有变量声明了一个类,一个公共void方法做了一些昂贵的操作来计算然后设置私有变量,另一个公共方法返回私人变量。 The question was "how can you make this thread-safe" and one possible answer was "synchronize each of the two methods" and one other possible answer was "this class can not be made thread-safe". 问题是“如何使这个线程安全”,一个可能的答案是“同步两个方法中的每一个”,另一个可能的答案是“这个类不能成为线程安全的”。

I figured the class could not be made thread-safe since even if you synchronize both methods, you could have a situation that Thread1 would invoke the setter and before Thread1 could invoke the getter, Thread2 might execute and invoke the setter, so that when Thread1 went and retrieved the result it would get the wrong info. 我认为类不能成为线程安全的,因为即使你同步这两个方法,你可能会遇到Thread1会调用setter的情况,在Thread1可以调用getter之前,Thread2可能会执行并调用setter,这样当Thread1去检索结果会得到错误的信息。 Is this the right way to look at things? 这是看待事物的正确方法吗? The book suggested the correct answer was that the class could be made thread safe by synchronizing the two methods and now I'm confused... 书中提出的正确答案是,通过同步这两种方法可以使类成为线程安全,现在我很困惑......

I figured the class could not be made thread-safe since even if you synchronize both methods, you could have a situation that Thread1 would invoke the setter and before Thread1 could invoke the getter, Thread2 might execute and invoke the setter, so that when Thread1 went and retrieved the result it would get the wrong info. 我认为类不能成为线程安全的,因为即使你同步这两个方法,你可能会遇到Thread1会调用setter的情况,在Thread1可以调用getter之前,Thread2可能会执行并调用setter,这样当Thread1去检索结果会得到错误的信息。 Is this the right way to look at things? 这是看待事物的正确方法吗?

You are correct with this. 你是对的。 There is no way to guarantee that a thread will not have called either of the methods in between your calls of each of the methods, from within the class . 无法保证线程不会在类中调用每个方法之间调用任何一种方法。

If you do want to do this, that will require a wrapper class. 如果你想这样做,那将需要一个包装类。 So if the class with the getter and setter is like so: 所以如果带有getter和setter的类是这样的:

class Foo
{
    private static int bar;

    public static synchronized void SetBar(int z) { ... }
    public static synchronized int GetBar() { ... }
}

The wrapper class would look something like this: 包装类看起来像这样:

class FooWrapper
{

    public synchronized int SetGetBar(int z)
    {
        Foo.SetBar(z);
        return Foo.GetBar();
    }

}

The only way to guarantee this will work is if you can guarantee that all calls will go through your wrapper class rather than directly to class Foo. 保证这一点的唯一方法是,如果你可以保证所有的调用都将通过你的包装类,而不是直接到类Foo。

When you make those two synchronized, the getter and setter themselves are thread-safe. 当你使这两个同步时,getter和setter本身是线程安全的。 More specifically: 进一步来说:

  • When you call the setter, you are guaranteed that the value of the variable is what you set it to when the method finishes. 当您调用setter时,可以保证变量的值是方法完成时设置的值。
  • When you call the getter, you are guaranteed that the return value is the value of the variable when you made the call. 当您调用getter时,可以保证返回值是您进行调用时变量的值。

    However, making the getter and setter themselves thread-safe does not mean that the application as a whole (ie whatever is using this class) is thread-safe. 但是,使getter和setter 本身是线程安全的并不意味着应用程序作为一个整体 (即使用此类的任何东西)是线程安全的。 If your thread wants to call a setter then get the same value upon invoking the getter, that involves synchronization on a different level. 如果您的线程想要调用setter,那么在调用getter时获取相同的值,这涉及在不同级别上的同步。

    As far as thread-safety is concerned, a thread-safe class need not control how its methods are invoked (for example, it need not control which way the threads interleave their calls), but it needs to ensure that when they are, the methods do what they are supposed to. 就线程安全性而言,线程安全类不需要控制如何调用其方法(例如,它不需要控制线程交换其调用的方式),但它需要确保它们何时 ,方法做他们应该做的事。

  • synchronized in Java is an object-wide lock. Java中的synchronized是一个对象范围的锁。 Only one synchronized method of any given object can be executed on any given thread at a time. 任何给定对象的一个synchronized方法一次只能在任何给定的线程上执行。 Let's have this class: 让我们来上课:

    class Foo
    {
        private int bar;
    
        public synchronized void SetBar() { ... }
        public synchronized int GetBar() { ... }
    }
    
    • Thread 1 calls SetBar() . 线程1调用SetBar() Thread 1 acquires the object lock. 线程1获取对象锁。
    • Thread 2 wants to call SetBar() , but Thread 1 holds the lock. 线程2想要调用SetBar() ,但线程1持有锁。 Thread 2 is now queued to acquire the lock when Thread 1 will release it. 线程2现在排队等待线程1将释放它时获取锁定。
    • Thread 1 finishes executing SetBar() and releases the lock. 线程1完成执行SetBar()并释放锁。
    • Thread 2 immediately acquires the lock and starts executing SetBar() . 线程2立即获取锁并开始执行SetBar()
    • Thread 1 calls GetBar() . 线程1调用GetBar() Thread 1 is now queued to acquire the lock when Thread 2 will release it. 线程1现在排队等待线程2将释放它时获取锁定。
    • Thread 2 finishes executing SetBar() and releases the lock. 线程2完成执行SetBar()并释放锁。
    • Thread 1 acquires the lock, executes GetBar() , and is done with it. 线程1获取锁,执行GetBar() ,并完成它。

    You did the work twice, but you didn't cause any race condition. 你做了两次工作,但没有造成任何竞争条件。 It may or may not be erroneous to do the work twice, depending on what it is. 两次工作可能会或可能不会是错误的,具体取决于它是什么。

    A frequent pattern is to have one thread produce content and one other thread do something useful with it. 一个常见的模式是让一个线程产生内容,另一个线程做一些有用的东西。 This is called the producer-consumer pattern. 这被称为生产者 - 消费者模式。 In this case, there is no confusion over who or what tries to SetBar() and what tries to GetBar() . 在这种情况下,对于谁或什么尝试SetBar()以及尝试GetBar()内容没有混淆。

    声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

     
    粤ICP备18138465号  © 2020-2024 STACKOOM.COM