使用ReentrantLock

时间:2017-05-21 08:05:43

标签: 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()方法。)?此外,(稍微偏离审查)是否有任何我可以遇到的真实场景?

注意:我在Code Review上发布了这个问题,但似乎对虚拟代码的审查是偏离主题的。

1 个答案:

答案 0 :(得分:1)

您可以使用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
        }
    }
}