在多线程中使用 ReentrantLock

Using ReentrantLock with Multiple threads

我是多线程的新手,非常感谢您的帮助。

已编辑代码和问题

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

厨师Class

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

餐厅Class(主)

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

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

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

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

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

此模式可确保代码始终保持 myLock 解锁状态,无论发生什么情况。重点是:

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

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

  • unlock() 调用在 finally {...} 内。这样可以确保无论发生什么情况,此代码片段都会使锁保持解锁状态。

看起来你的方法每个锁定互斥体两次,但只解锁一次。他们这样做:

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() 都是 public 函数。我们希望其他 类 能够调用其中任何一个,但我们也希望它们都锁定 myMutex,我们希望 bizzwizz() 能够调用 foobar().

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

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