代码之家  ›  专栏  ›  技术社区  ›  Vipin

ArrayList方法导致生产者-消费者中的死锁

  •  1
  • Vipin  · 技术社区  · 6 年前

    这是Java中的生产者-消费者场景。制作人在ArrayList中写作,而消费者则从中阅读。两者都在添加或删除元素之前锁定ArrayList对象。

    你能帮我弄明白为什么它会引起恶作剧吗?

    如果我在System.out.println(…)之后放置remove调用,它工作得很好(无限生产者和消费者都工作)。

    buffer.remove(buffer.size()-1);
    System.out.println("Consumed" + " " + buffer.get(buffer.size() - 1) + " size " + buffer.size());
    

    完整代码:

    public class Test {
    
        public static void main(String[] args) {
    
            ArrayList<Integer> arrayList = new ArrayList<>(10);
    
            Producer producer = new Producer(arrayList);
            Consumer consumer = new Consumer(arrayList);
            ExecutorService executorService = Executors.newFixedThreadPool(2);
            executorService.execute(producer);
            executorService.submit(consumer);
        }
    }
    class Producer implements Runnable{
    
        private final ArrayList<Integer> buffer;
        public Producer(ArrayList<Integer> arrayList){
            this.buffer = arrayList;
        }
    
        public void run(){
            while (true) {
                synchronized (buffer) {
                    while (isFull(buffer)) {
                        try {
                            buffer.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    buffer.add(1);
                    System.out.println("Produced" + " " + buffer.get(buffer.size() - 1) + " size " + buffer.size());
                    buffer.notifyAll();
                }
            }
        }
    
        private boolean isFull(ArrayList<Integer> buffer) {
            return buffer.size() == 10;
        }
    }
    class Consumer implements  Runnable{
    
        private final ArrayList<Integer> buffer;
    
        public Consumer(ArrayList<Integer> buffer) {
            this.buffer = buffer;
        }
    
        public void run(){
            while (true) {
                synchronized (buffer) {
                    while (isEmpty(buffer)) {
                        try {
                            buffer.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    buffer.remove(buffer.size()-1);
                    System.out.println("Consumed" + " " + buffer.get(buffer.size() - 1) + " size " + buffer.size());
                    buffer.notifyAll();
                }
            }
        }
    
        private boolean isEmpty(ArrayList<Integer> buffer) {
            return buffer.size() == 0;
        }
    }
    

    典型的输出在死锁的情况下,生产者释放锁后消费线程不会开始处理。

    Produced 1 size 1
    Produced 1 size 2
    Produced 1 size 3
    Produced 1 size 4
    Produced 1 size 5
    Produced 1 size 6
    Produced 1 size 7
    Produced 1 size 8
    Produced 1 size 9
    Produced 1 size 10
    Consumed 1 size 9
    Consumed 1 size 8
    Consumed 1 size 7
    Consumed 1 size 6
    Consumed 1 size 5
    Consumed 1 size 4
    Consumed 1 size 3
    Consumed 1 size 2
    Consumed 1 size 1
    Produced 1 size 1
    Produced 1 size 2
    Produced 1 size 3
    Produced 1 size 4
    Produced 1 size 5
    Produced 1 size 6
    Produced 1 size 7
    Produced 1 size 8
    Produced 1 size 9
    Produced 1 size 10
    

    JDK:jdk1.8.0_111型

    下面是生产者和消费者线程的线程转储

    "pool-1-thread-2" #11 prio=5 os_prio=31 tid=0x00007fb1e8039800 nid=0x3c03 waiting on condition [0x000070000fa72000]
       java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000007aac942a0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    
    "pool-1-thread-1" #10 prio=5 os_prio=31 tid=0x00007fb1e704d800 nid=0x3b03 in Object.wait() [0x000070000f96f000]
       java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000007aac8a998> (a java.util.ArrayList)
        at java.lang.Object.wait(Object.java:502)
        at com.vipin.threading.Producer.run(Test.java:36)
        - locked <0x00000007aac8a998> (a java.util.ArrayList)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    

    2 回复  |  直到 6 年前
        1
  •  3
  •   Radiodef    6 年前

    buffer.get(buffer.size() - 1) 当列表为空时引发异常。

    您可以使用 Future.get :

    try {
        executorService.submit(consumer).get();
    } catch (ExecutionException | InterruptedException e) {
        e.printStackTrace();
    }
    
        2
  •  1
  •   xingbin    6 年前

    IndexOutOfBoundsException 因为你试图从一个空列表中检索。

    Consumer 并打印例外:

        try {
            while (true) {
                synchronized (buffer) {
                    while (isEmpty(buffer)) {
                        try {
                            buffer.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    buffer.remove(buffer.size()-1);
    
                    System.out.println("Consumed" + " " + buffer.get(buffer.size() - 1) + " size " + buffer.size());
    
                    buffer.notifyAll();
                }
            }
        } catch (Exception e) {
            System.out.println(e);
        }
    

    java.lang.IndexOutOfBoundsException: Index -1 out-of-bounds for length 0