Использование ReentrantLock с несколькими потоками - PullRequest
0 голосов
/ 09 ноября 2019

Я новичок в многопоточности, и буду очень признателен за помощь.

РЕДАКТИРОВАННЫЙ КОД И ВОПРОС

Я хочу использовать класс ReentrantLock для управления доступом к двум спискам ArrayLists,Первый - это OrderList, а второй - подготовленный ArrayList для Ordders. Два объекта Class Chef (который расширяет Thread) хотят каждый раз удалять заказ из списка массивов, спать в течение произвольного промежутка времени, а затем размещать заказ в массиве readyOrders до тех пор, пока 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

Почему не разблокируется первый поток?

Ответы [ 2 ]

1 голос
/ 09 ноября 2019

Похоже, что каждый из ваших методов дважды блокирует мьютекс, но разблокирует его только один раз. Они делают что-то вроде этого:

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(). Эта ситуация иногда называется self deadlock .

A reentrant mutex позволяет одному потоку иметь несколько утверждений на мьютекс. Каждый вызов lock() увеличивает количество заявок на единицу, а каждый вызов unlock() уменьшает его. Потоки B, C и D не смогут lock() мьютекс, если поток A имеет как минимум одно утверждение.

0 голосов
/ 09 ноября 2019

Это не ответ на ваш вопрос. Я не уверен, что ваш вопрос, но это то, что вы должны знать.

Если вы собираетесь использовать Lock объекты, используйте их так:

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

Этот шаблон гарантирует, что код всегда будет myLock разблокирован, независимо от того, что еще произойдет. Важные моменты:

  • Вызов lock() находится вне оператора try { ... } finally { ... }. Обычно мы не ожидаем броска вызова lock(), но если сделал бросок, было бы ошибкой впоследствии пытаться разблокировать его.

  • Ничегоостальное происходит между вызовом lock() и try. Как только lock() успешно вернется, мы хотим убедиться, что Exception или Error не могут быть выброшены, чтобы обойти предложение finally {...}.

  • unlock() звонок внутри finally {...}. Это гарантирует, что независимо от того, что еще произойдет, этот фрагмент кода оставит блокировку разблокированной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...