java - 使用 ReentrantLock 避免死锁

标签 java multithreading thread-safety locking

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

A.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);
        }
    }
}

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

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

关于java - 使用 ReentrantLock 避免死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44094584/

相关文章:

java - 获取直接字节/短整型/整数/浮点缓冲区的一段

不允许声明 Java 变量

java - 当队列已满时 Spring 线程和 TaskRejectException

c++ - 如果在promise.set_value()之后调用future.get()会发生什么?

c++ - 锁定互斥锁以仅返回一个值是否明智?

android - AsyncTask 未在 UI 线程上更新

java - Tomcat - 单击 Web 服务获取 "The requested resource is not available"

python - selenium python 中的多线程

java - 尝试在可运行对象中同步方法

java - SWIG 和异常 : avoid using throw(Exception) in C++