使用ReentrantLock避免死锁



我正在做一些练习作业,并涉猎一些虚拟代码,试图更好地理解线程和锁的概念。下面是一段(有时(陷入死锁的代码。

答.java

public class A {
    private B b;
    public void setB(B b) {
        this.b = b;
    }
    public synchronized void foo(boolean callBar) {
        System.out.println("foo");
        if (callBar) {
            b.bar(false);
        }
    }
}

B.java

public class B {
    private A a;
    public void setA(A a) {
        this.a = a;
    }
    public synchronized void bar(boolean callFoo) {
        System.out.println("bar");
        if (callFoo) {
            a.foo(false);
        }
    }
}

演示.java

public class Demo {
    public static void main(String[] args) {
    A a = new A();
    B b = new B();
    a.setB(b);
    b.setA(a);
    new Thread(() -> {
        a.foo(true);
    }).start();
    new Thread(() -> {
        b.bar(true);
    }).start();
    }
}

解决方案:我用Lock而不是synchronized

答.java

public class A {
    private final ReentrantLock lock = new ReentrantLock();
    private B b;
    public void setB(B b) {
        this.b = b;
    }
    public ReentrantLock lock() {
        return lock;
    }
    public boolean impendingExecute() {
        Boolean thisLock = false;
        Boolean otherLock = false;
        try {
            thisLock = lock.tryLock();
            otherLock = b.lock().tryLock();
        } finally {
            if (!(thisLock && otherLock)) {
                if (thisLock) {
                    lock.unlock();
                }
                if (otherLock) {
                    b.lock().unlock();
                }
            }
        }
        return thisLock && otherLock;
    }
    public void foo(boolean callBar) {
        System.out.println("foo");
        if (callBar && impendingExecute()) {
            try {
                b.bar(false);
            } finally {
                lock.unlock();
                b.lock().unlock();
            }
        }
    }
}

B.java

public class B {
    private final ReentrantLock lock = new ReentrantLock();
    private A a;
    public void setA(A a) {
        this.a = a;
    }
    public ReentrantLock lock() {
        return lock;
    }
    public boolean impendingExecute() {
        Boolean thisLock = false;
        Boolean otherLock = false;
        try {
            thisLock = lock.tryLock();
            otherLock = a.lock().tryLock();
        } finally {
            if (!(thisLock && otherLock)) {
                if (thisLock) {
                    lock.unlock();
                }
                if (otherLock) {
                    a.lock().unlock();
                }
            }
        }
        return thisLock && otherLock;
    }
    public void bar(boolean callFoo) {
        System.out.println("bar");
        if (callFoo && impendingExecute()) {
            try {
                a.foo(false);
            } finally {
                lock.unlock();
                a.lock().unlock();
            }
        }
    }
}

进行上述更改后,代码不会导致死锁。这是实现这一点的正确方法吗(基本上,我希望审查impendingExecute()方法。另外,(与评论有点偏离(我会遇到任何现实世界的情况吗?

注意:我已经在代码审查上发布了这个问题,但似乎对虚拟代码的审查是题外话。

您可以使用

java.util.concurrent.locks.ReentrantLock .此设计允许该方法尝试获取两个类的锁,在失败时释放锁,并在必要时重试。如果需要尝试直到成功,则需要将其置于循环中并使用某些策略终止。

while (true) {
    if (this.lock.tryLock()) {
        try {
          if (ba.lock.tryLock()) {
            try {
             //some logic
              break;
            } finally {
              ba.lock.unlock();
            }
          }
        } finally {
          this.lock.unlock();
        }
    }
    int n = number.nextInt(1000);
    int TIME = 1000 + n; // 1 second + random delay to prevent livelock
    Thread.sleep(TIME);
}

或者,您可以使用此解决方案,确保以相同的顺序获取和释放多个锁:

if (compareTo(ba) < 0) {
        former = this;
        latter = ba;
    } else {
        former = ba;
        latter = this;
    }
    synchronized (former) {
        synchronized (latter) {
            //Some logic
        }
    }
}

最新更新