【问题标题】:Using ReentrantLock with Multiple threads多线程使用 ReentrantLock
【发布时间】:2019-11-08 22:23:10
【问题描述】:

我是多线程新手,非常感谢一些帮助。

编辑的代码和问题

我想使用 ReentrantLock 类来控制对两个 ArrayList 的访问。第一个是orderList,而第二个是preparedOrders ArrayList。类 Chef(扩展 Thread)的两个对象每次都希望从数组列表中删除一个订单,随机休眠一段时间,然后将订单放入preparedOrders 数组列表中,直到 OrdersList 为空。这是我当前的代码:

厨师班

import java.util.concurrent.locks.ReentrantLock;

public class Chef extends Thread{

    private String name;
    private ReentrantLock orderLock; 
    private ReentrantLock preparedLock;
    private String orderName;
    private Object useForLocking = new Object();

    public Chef(String name, ReentrantLock orderLock, ReentrantLock preparedLock){
        this.name = name;
        this.orderLock = orderLock; 
        this.preparedLock = preparedLock;
    }

    public void run(){
        while(Restaurant.orderList.size()>0){
            try{
                getOrder();
                System.out.println(name +" is preparing "+orderName+"\n");
                Thread.sleep(Math.round(Math.random()*100));
                outputOrder();
            }catch(Exception e){
                System.out.println("run exception: "+e+"\n");
            }
        }
    }

    public void getOrder(){
        if(orderLock.tryLock()){
            orderLock.lock();
            try{
                orderName = Restaurant.orderList.remove(0);
            }catch(Exception e){
                System.out.println("getOrder exception: "+e+"\n");
            }finally{
                orderLock.unlock();
                synchronized (useForLocking){
                    useForLocking.notify();
                }
            }
        }else{
            try{
                synchronized (useForLocking){
                    useForLocking.wait();
                }
            }catch(Exception e){
                System.out.println("getOrder wait exception: "+e+"\n");
            }
        }
    }

    public void outputOrder(){
        if(preparedLock.tryLock()){
            preparedLock.lock();
            try{
                Restaurant.preparedOrders.add(orderName);
            }catch(Exception e){
                System.out.println("outputOrder exception: "+e+"\n");
            }finally{
                preparedLock.unlock();
                synchronized (useForLocking){
                    useForLocking.notify();
                }
            }
        }else{
            try{
                synchronized (useForLocking){
                    useForLocking.wait();
                }
            }catch(Exception e){
                System.out.println("outputOrder wait exception: "+e+"\n");
            }
        }
    }
}

餐厅类(主)

import java.util.concurrent.locks.ReentrantLock;
import java.util.ArrayList;

 public class Restaurant{
    public static ArrayList<String> orderList = new ArrayList<String>();
    public static ArrayList<String> preparedOrders = new ArrayList<String>();

    public static void main(String[] args) {

        orderList.add("Cheese Pizza");
        orderList.add("Hotdog");
        orderList.add("Hamburger");
        orderList.add("Cheese burger");
        orderList.add("Chicken Nuggets");
        orderList.add("Chicken Burger");

        ReentrantLock orderLock = new ReentrantLock();
        ReentrantLock preparedLock = new ReentrantLock();

        Chef c1 = new Chef("Chef John", orderLock, preparedLock);
        Chef c2 = new Chef("Chef Mark", orderLock, preparedLock);

        c1.start();
        c2.start();
    }
 }

输出

Chef John is preparing Cheese Pizza
Chef John is preparing Hotdog
Chef John is preparing Hamburger
Chef John is preparing Cheese burger
Chef John is preparing Chicken Nuggets
Chef John is preparing Chicken Burger

为什么第一个线程没有解锁?

【问题讨论】:

  • 这不是一个非常简洁的问题。 stackoverflow.com/help/minimal-reproducible-example / IllegalMonitorStateException 被抛出,因为在对象上调用 notifyAllwait 的前提是该特定对象被 synchronized 锁定。对于java.util.concurrent.locks,您需要使用其他方法。
  • @TomHawtin-tackline 你能提供一些示例代码吗?
  • @TomHawtin-tackline 我已经编辑了我的代码(这次只关注厨师),但它仍然没有提供想要的功能。有什么帮助吗?
  • Condition API 文档中有一个使用带有 Locks 的监控方法的示例。 docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/… 你需要用Lock.newCondition 创建一个Condition 并确保你调用await(如wait)和signalAll(如notifyAllCondition 的同一个实例上.
  • @NathanHughes 显然。如果这是一个简单的错误,我可以自己修复它。

标签: java multithreading arraylist locking


【解决方案1】:

看起来好像您的方法每个都将互斥锁锁定了两次,但只解锁了一次。他们会这样做:

if(preparedLock.tryLock()) {   // locks the mutex if the result is true.
    preparedLock.lock();       // This locks it A SECOND TIME.
    try{
        ...
    }finally{
        preparedLock.unlock(); // This unlocks it ONCE.
    }
}
// At this point, the thread still has one lock on preparedLock

ReentrantLock 对象允许单个线程持有对互斥体的多个声明。这对于这样的情况很有用:

private final Lock myMutex = new ReentrantLock();

public void foobar(...) {
    myMutex.lock();
    try {
        ...
    }
    finally {
        myMutex.unlock();
    }
}

public void bizzwizz(...) {
    myMutex.lock();
    try {
        ...
        foobar(...);
        ...
    }
    finally {
        myMutex.unlock();
    }
}

foobar() 和 bizzwizz() 都是公共函数。我们希望其他类能够调用其中任何一个,但我们也希望它们都锁定myMutex,并且我们希望bizzwizz() 能够调用foobar()

如果 myMutex 不是可重入的,当bizzwizz() 调用foobar() 时我们会遇到问题,因为foobar() 会尝试锁定已经被锁定的互斥锁。通常情况下,lock() 调用在拥有它的线程释放锁之前不会返回。但这永远不会发生,因为拥有它的线程就是调用lock() 的线程。这种情况有时被称为自死锁

可重入互斥体允许单个线程对互斥体拥有多个声明。每个lock() 调用将声明数量增加一,每个unlock() 调用将其递减。只要线程 A至少有一个声明,线程 B、C 和 D 将无法 lock() 互斥锁。

【讨论】:

  • 哦,我虽然 tryLock() 只是检查了锁而没有锁。
  • @MohamedMoustafa,tryLock() 的返回值告诉它是成功(锁定锁)还是失败。还有一个isLocked() 方法,但isLocked() 不是很有用,除非在调试或性能监控代码中。 isLocked() 的问题在于,无论它返回什么值,即使在一纳秒后你也不能依赖它仍然为真:另一个线程可能在 isLocked() 之间的那一刻获得了锁或释放了锁看着它,以及你的代码决定对它做些什么的那一刻。
【解决方案2】:

这不是您问题的答案。我不确定你的问题是什么,但这是你应该知道的。

如果您要使用 Lock 对象,请像这样使用它们:

myLock.lock();
try {
    doSomethingThatNeedsProtection();
}
finally {
    myLock.unlock();
}

此模式可确保代码始终保持myLock 未锁定,无论发生什么其他情况。重点是:

  • lock() 调用在 try { ... } finally { ... } 语句之外。我们通常不期望lock() 调用会抛出,但如果确实 抛出,随后尝试解锁它是错误的。

  • lock() 调用和try 之间没有其他内容。一旦lock() 成功返回,我们要确保不会抛出任何会绕过finally {...} 子句的ExceptionError

  • unlock() 调用位于 finally {...} 内部。这样可以确保无论发生什么其他情况,这段代码 sn-p 都会使锁保持解锁状态。

【讨论】:

    猜你喜欢
    • 2015-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-22
    • 2015-09-04
    • 2012-01-07
    • 2014-01-12
    相关资源
    最近更新 更多