Как реализовать общий буфер между потоками Java?

Я намеревался написать общий буфер между потоком-производителем и потоком-потребителем. Вот мои коды:

class PQueue
{
    Token token;
    boolean flag = false;   // false: add, true: poll

    PQueue()
    {
        token = null;
    }

    synchronized void add(Token token)
    {
        if(flag == true)
        {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        flag = true;
        notify();
        this.token = token;
    }

    synchronized Token poll()
    {
        if(flag == false)
        {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        flag = false;
        notify();
        return this.token;
    }
}

Я новичок в многопоточности. Есть ли потенциальные ошибки параллелизма? Является ли это «стандартным/общим» способом достижения этой цели? Или есть более простой и эффективный способ?


person JackWM    schedule 14.08.2012    source источник
comment
Разве вы не можете просто использовать java.util.concurrent.BlockingQueue?   -  person vanza    schedule 14.08.2012
comment
notify() внутри синхронизированного блока выглядит подозрительно для меня (но не могу сказать, работает ли это или приводит к взаимоблокировкам)   -  person Andreas Dolk    schedule 14.08.2012
comment
@Andreas_D: ты не можешь называть notify() по-другому, что ты имеешь в виду под подозрением? (Его код подозрительный по другим причинам, но не по этой.)   -  person vanza    schedule 14.08.2012
comment
@vanza Это хорошее предложение. В моем случае мне нужен только одноэлементный буфер. Есть ли у BlockingQueue аналогичная реализация?   -  person JackWM    schedule 14.08.2012
comment
@vanza - краткий обзор Google напомнил мне, что ты прав. notify должен вызываться внутри синхронизированного блока.   -  person Andreas Dolk    schedule 14.08.2012


Ответы (2)


Взгляните на java.util.concurrent, в частности пакет BlockingQueue Интерфейс и классы, которые его реализуют. Они предназначены для передачи сообщений из одного потока в другой. SynchronousQueue — это именно то, что вам нужно. повторно пытаюсь реализовать.

Ваша собственная реализация имеет несколько недостатков. Во-первых, обе общие переменные должны быть объявлены volatile, чтобы гарантировать, что изменения в одном потоке видны другому. И ваши if (flag == false) и if (flag == true) тесты должны быть while циклами, потому что wait() может ложно просыпаться, когда notify() на самом деле не звонили.

Вместо того, чтобы иметь отдельную переменную флага, я бы предложил просто установить для токена значение null, чтобы указать, что объекта нет. И вместо того, чтобы ловить, печатать и слепо продолжать работу перед лицом InterruptedException, я бы рекомендовал позволить обоим методам просто генерировать это исключение, если оно произойдет. Это блокирующие методы, и вызывающая сторона несет ответственность за обработку возможности прерывания блокирующего метода.

Кроме того, я не знаю, какой у вас класс Token, но на самом деле ничто в вашей очереди не зависит от его типа. Было бы разумнее определить общий PQueue<T>, а затем использовать PQueue<Token>, если вы хотите передать токены.

person Wyzard    schedule 14.08.2012

1. Попробуйте использовать thread-safe классы и интерфейсы из пакета java.util.concurrent.

2. Используйте BlockingQueue Interface вместе с ArrayBlockingQueue Class.

person Kumar Vivek Mitra    schedule 14.08.2012