自定义数组阻塞队列

时间:2017-05-23 05:18:33

标签: java blockingqueue

我正在尝试实现阻塞队列功能,但Thread进入等待状态。无法弄清楚可能出现的问题。我尝试了一些在线实现,但都没有。也许我的执行者代码是错误的。但如果我用MyBlockingQueue替换ArrayBlockingQueue,一切正常。

以下是两种主要方法。

public synchronized void put(Integer i) throws InterruptedException {

    if (a.size() == capacity) {
        wait();
    }
    a.add(i);
    notifyAll();
}

public synchronized void take() throws InterruptedException {

    if (a.isEmpty()) {
        wait();
    }
    a.remove(0);
    notifyAll();
}

代码:

public class App {

    public static MyBlockingQueue q = new MyBlockingQueue(10);

    // public static ArrayBlockingQueue q = new ArrayBlockingQueue(10);

    public void method1() throws InterruptedException {
        for (int i = 0; i < 20; i++) {
            q.put(i);
            System.out.println(q);
        }
    }

    public void method2() throws InterruptedException {
        for (int i = 0; i < 20; i++) {
            q.take();
        }
    }

    public static void main(String[] args) throws InterruptedException {

        App a = new App();

        ExecutorService executor1 = Executors.newFixedThreadPool(20);
        ExecutorService executor2 = Executors.newFixedThreadPool(20);

        for (int i = 0; i < 2; i++) {

            executor1.submit(new Runnable() {

                public void run() {

                    try {
                        a.method1();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }

        for (int i = 0; i < 2; i++) {

            executor2.submit(new Runnable() {

                public void run() {

                    try {
                        a.method2();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            });
        }

        executor1.shutdown();
        executor2.shutdown();

        executor1.awaitTermination(1, TimeUnit.DAYS);
        executor2.awaitTermination(2, TimeUnit.DAYS);

        System.out.println();
        System.out.println("The final queue is:");
        System.out.println(App.q);

    }

}

class MyBlockingQueue {

private ArrayList<Integer> a;
    private int capacity;

    public MyBlockingQueue(Integer cap) {
        capacity = cap;
        a = new ArrayList<Integer>(capacity);
    }

    @Override
    public String toString() {
        String output = "";
        for (Integer i : a) {
            output += i.toString() + " ";
        }
        return "[" + output + "]";
    }

    public synchronized void put(Integer i) throws InterruptedException {

        if (a.size() == capacity) {
            wait();
        }
        a.add(i);
        notifyAll();
    }

    public synchronized void take() throws InterruptedException {

        if (a.isEmpty()) {
            wait();
        }
        a.remove(0);
        notifyAll();
    }
}

3 个答案:

答案 0 :(得分:0)

下面是坐在角落里的害羞的罪魁祸首,还需要将if修复为while

@Override
public String toString() {
    String output = "";
    for (Integer i : a) {
        output += i.toString() + " ";
    }
    return "[" + output + "]";
}

我在解决方案中添加了toString方法,但它们无法正常工作。

synchronized事情正常。感谢@Maurice提出这个建议。

答案 1 :(得分:0)

问题是所有线程都在同一个监视器上等待,并且会被notifyAll唤醒并同时运行。

您可以使用方法notify唤醒单个线程并进行单次插入或删除。但是notify()内部调用的put()可以唤醒线程,等待not full方法中的put()条件插入:

while (a.size() == capacity) {
        wait(); // many threads waiting
    }

notify()内调用的take()可以唤醒等待not empty条件的线程:

while (a.isEmpty()) {
        wait();
    }

因为所有线程都使用一个监视器,notify可以唤醒任何等待的线程。

所以你需要两个监视器:一个用于not full条件,一个用于not empty

    Object notFull = new Object();
    Object notEmpty = new Object();

    public synchronized void put(Integer i) throws InterruptedException {

        while (a.size() == capacity) {
            notFull.wait(); 
        }
        a.add(i);
        notEmpty.notify(); //wake up one random thread in take() method
    }

    public synchronized void take() throws InterruptedException {

        if (a.isEmpty()) {
            notEmpty.wait();
        }
        a.remove(0);
        notFull.notify(); // wake up one random thread in put() method
    }

现在notEmpty.notify()notFull.notify()不会在synchronizedput方法中释放take关键字获取的锁定。

我们需要在同一个锁上同步这两个方法并根据两个条件释放或获取它:not full和not empty。为此,有java.util.concurrent.locks.ReentrantLock类:

  

具有相同基本行为的重入互斥锁定   语义作为使用synchronized访问的隐式监视器锁   方法和陈述,但具有扩展功能。

此类表示具有条件的锁。它的方法[newCondition][2]创建条件:

  

返回与此Lock实例一起使用的Condition实例。

Condition允许通过await()方法挂起多个线程,并通过signal()方法唤醒单个等待线程。没有ReentrantLock就无法使用它。 调用Condition.await时,ReentrantLock.lock方法中获取的线程释放锁定。调用Condition.signal时,等待线程获取ReentrantLock。 最终代码:

/** Main lock guarding all access */
       private final ReentrantLock lock;
       /** Condition for waiting takes */
       private final Condition notEmpty;
       /** Condition for waiting puts */
       private final Condition notFull;

 public void put(Integer i) throws InterruptedException {
          lock.lockInterruptibly();
          try {
                  while (a.size() == capacity)
                      notFull.await();//realease monitor and sleep until notFull.signal is called

             a.add(i);
            notEmpty.signal();// wake up one random thread in take() method
          } finally {
              lock.unlock();
          }
 }

public void take() throws InterruptedException {
          lock.lockInterruptibly();
          try {
                   while (a.isEmpty())
                      notEmpty.await();//realease monitor and sleep until notEmpty.signal is called

                   a.remove(0);
             notFull.signal();// wake up one random thread in put() method
          } finally {
              lock.unlock();
          }       
}

ReentrantLock确保只有一个线程可以同时执行puttake个方法。 Condition允许根据条件暂停和恢复线程。

答案 2 :(得分:-3)

放入和接收ArrayBlockingQueue的方法应与java.util.concurrent.locks.ReentrantLock同步。

示例:

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            // your logic 
        } finally {
            lock.unlock();
        }
    }