关于线程安全代码的问题

发布于 2024-11-27 13:47:18 字数 408 浏览 2 评论 0原文

我有一个类如下

public MyClass{

Boolean flag = false;

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

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

两个方法都在对象标志上同步。现在我的疑问是两个不同的线程是否可以同时执行同步块(1&2)。 会出现以下情况吗? 1)线程1正在设置标志值,线程2同时获取其值?

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).
Can the following situation arise?
1)Thread 1 is setting flag value and thread 2 getting its value at same time?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

逆流 2024-12-04 13:47:18

是的,可以。请注意,您正在同步您正在设置的同一对象。因此,setter 可以将对象引用更改为不同的对象,然后 getter 可以在新对象上同步,而 setter 仍在同步块内。

但还有更多:flag(通常)是对系统范围单例 Boolean.TRUEBoolean.FALSE 之一的引用,因此即使不以任何方式提及您的班级,(至少理论上)也可以在班级之外锁定这些内容。在这种情况下,您可能会陷入僵局,并且很难找出原因。

(另请注意,当前形式的代码是错误的,因为 setter 没有参数,因此 this.flag = flag 将引用分配给自身 - 但上面我假设您的意思是它的行为类似于一个普通的 setter :)

修复方法是使用专用的 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;
       }
    }
}

如果你不担心关于您内部使用的同一个锁上的其他同步,您可以简单地使您的方法同步(在这种情况下,它们锁定this)。

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.

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. 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 :)

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):

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).

清晰传感 2024-12-04 13:47:18

我假设你的 setFlag 方法实际上应该有一个参数并且没有返回值?

对我来说这看起来是个坏主意。

  • Flag 设置为引用 r1
  • 线程 1 调用 setFlag(r2)
  • 线程 1 获取 r1 上的锁 线程
  • 1 设置 flag 为引用 r2
  • 线程 2 获取 r2 上的锁
  • 两个线程实际上是并发执行的,都在同步块中,但锁定在不同的对象上...

基本上,我认为在可变字段上同步是一个坏主意。有一个最近关于此的问题,您可能会感兴趣。

我会使用这种设计:(

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;
    }
}

或者可能只使用易失性字段。这实际上取决于类中的其他内容。)

I'm assuming your setFlag method should actually have a parameter and no return value?

This looks like a bad idea to me.

  • Flag is set to reference r1
  • Thread 1 calls setFlag(r2)
  • Thread 1 acquires lock on r1
  • Thread 1 sets flag to reference r2
  • Thread 2 acquires lock on 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.)

病毒体 2024-12-04 13:47:18

问题可能出在方法 setFlag 中 - 它将更改“锁定对象”以进行同步。您必须确保在同一对象上同步。
私有对象锁 = new Object();
并同步对象锁。

Problem can be in method setFlag - it will change "lock object" for synchronization. You must be sure that you synchronize on same object.
private Object lock = new Object();
And synchronize on object lock.

深白境迁sunset 2024-12-04 13:47:18

那是行不通的。您正在阻止标志引用的对象的监视器。想象一下,如果一个线程进入setter,锁定当前的flag对象,然后flag指向一个新的Flag。然后第二个线程进入 getter,并且可以自由地获取标志上的锁,因为它现在指向另一个对象。

因此,您可以有两个线程看似都锁定在“标志”上,但锁定在不同的对象上。这就是为什么用于锁定的对象通常应声明为final,以避免出现这种情况。

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. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文