简体   繁体   English

有关线程安全代码的问题

[英]Question about thread safe code

I have a class as follows 我的课如下

public MyClass{

Boolean flag = false;

    public Boolean getflag(){
       synchronized(flag){
          //BLOCK 1
          return flag;
       }
    }

    public Boolean setflag(){
       synchronized(flag){
           //BLOCK 2
           this.flag = flag;
       }
    }
}

Both methods are synchronized on object flag.Now my doubt is can two different threads can simultaniously executed the synchronized blocks (1&2). 两种方法都在对象标志上同步,现在我的疑问是两个不同的线程能否同时执行同步块(1&2)。 Can the following situation arise? 会出现以下情况吗? 1)Thread 1 is setting flag value and thread 2 getting its value at same time? 1)线程1正在设置标志值,线程2同时获得其值?

Yes it can. 是的,它可以。 Note that you are synchronizing on the same object you are setting . 请注意, 您正在设置的同一对象上进行同步 So the setter can change the object reference to something different, then the getter can synchronize on the new object while the setter is still inside the synchronized block. 因此,setter可以将对象引用更改为其他内容,然后getter可以在setter仍位于同步块内时在新对象上进行同步。

But there is more: flag is (usually) a reference to one of the system-wide singletons Boolean.TRUE and Boolean.FALSE , so it is (at least theoretically) possible to lock on these outside of your class, even without referring to your class in any way. 但是还有更多: flag通常是对系统范围内的单例Boolean.TRUEBoolean.FALSE之一的引用,因此(至少在理论上)可以在类之外锁定它们,即使不引用您的课程以任何方式。 In which case you can end up with a deadlock and you can have a hard time figuring out why. 在这种情况下,您可能会陷入僵局,并且很难找出原因。

(Note also that the code in its current form is wrong, since the setter has no parameter, thus this.flag = flag assigns the reference to itself - but above I assume that you meant it to behave like a normal setter :) (还请注意,当前格式的代码是错误的,因为设置器没有参数,因此this.flag = flag将引用分配给自身-但上面我假设您的意思是它像普通的设置器一样工作:)

The fix is to use a dedicated, private final lock object (in case you want to absolutely ensure that noone outside can synchronize on the same lock you are using within your class - which I suppose your original intention was): 解决方法是使用专用的private final锁定对象(以防万一您想绝对确保外面没有人可以在您的类中使用的同一锁上进行同步-我想您的初衷是):

public MyClass{

    private final Object lock = new Object();
    private Boolean flag = false;

    public Boolean getflag(){
       synchronized(lock){
          //BLOCK 1
          return flag;
       }
    }

    public void setflag(Boolean flag){
       synchronized(lock){
           //BLOCK 2
           this.flag = flag;
       }
    }
}

If you aren't worried so much about other synchronizing on the same lock you internally use, you can simply make your methods synchronized (in which case they lock on this ). 如果您不太担心其他在内部使用的同一锁上进行同步,则可以简单地使您的方法synchronized (在这种情况下,它们将在this锁)。

I'm assuming your setFlag method should actually have a parameter and no return value? 我假设您的setFlag方法实际上应该有一个参数,没有返回值?

This looks like a bad idea to me. 对我来说,这似乎是个坏主意。

  • Flag is set to reference r1 标志设置为参考r1
  • Thread 1 calls setFlag(r2) 线程1调用setFlag(r2)
  • Thread 1 acquires lock on r1 线程1在r1上获得锁
  • Thread 1 sets flag to reference r2 线程1将标志设置为引用r2
  • Thread 2 acquires lock on r2 线程2在r2上获得锁
  • Both threads are actually executing concurrently, both in a synchronized block, but locking on different objects... 两个线程实际上都在一个同步块中同时执行,但是锁定在不同的对象上。

Basically, I think it's a bad idea to synchronize on a mutable field. 基本上,我认为在可变字段上进行同步是一个坏主意。 There's a recent question about this which you may find interesting. 最近有一个与此有关的问题 ,您可能会发现它很有趣。

I would use this design instead: 我会改用这种设计:

private final Object lock = new Object();
private boolean flag;

public void setFlag(boolean flag) {
    synchronized (lock) {
        this.flag = flag;
    }
}

public boolean getFlag() {
    synchronized (lock) {
        return flag;
    }
}

(Or just use a volatile field, potentially. It really depends on what else is in the class.) (或者可能仅使用可变字段。这实际上取决于类中的其他内容。)

Problem can be in method setFlag - it will change "lock object" for synchronization. 问题可能出在方法setFlag中-它会更改“锁定对象”以进行同步。 You must be sure that you synchronize on same object. 您必须确保在同一对象上进行同步。
private Object lock = new Object(); 私有对象锁= new Object(); And synchronize on object lock. 并同步对象锁定。

Thats not going to work. 那是行不通的。 You're blocking on the monitor of the object referenced by flag. 您正在阻塞标志所引用的对象的监视器。 Imagine if a thread enters the setter, locks on the current flag object, and then flag is pointed at a new Flag. 想象一下,如果某个线程进入了setter,锁定了当前的flag对象,然后将flag指向一个新的Flag。 Then a second thread enters the getter, and is free to obtain a lock on flag, since it now points to a different object. 然后,第二个线程进入吸气剂,并可以自由获取标志锁定,因为它现在指向另一个对象。

Hence you can have two threads both seemingly locked on 'flag', but on different objects. 因此,您可以有两个线程似乎都锁定在“标志”上,但锁定在不同的对象上。 Thats why objects used for locking should generally be declared final, to avoid this situation arising. 因此,通常应将用于锁定的对象声明为final,以避免出现这种情况。

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

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