簡體   English   中英

Java中的線程生產者使用者

[英]Threads producer consumer in java

下面是消費者生產者問題代碼,但是該代碼無法正常工作。 在這里,消費者和生產者應該只是生產和消費一個對象。

public class ProducerConsumer {
    private static LinkedList<Integer> linkedList = new LinkedList<>();

    public static void main(String a[]) throws InterruptedException {
        Thread producer = new Thread(new Runnable() {

            @Override
            public void run() {
                synchronized(this) {
                    while (linkedList.size() == 1) {
                        try {
                            wait();
                        } catch(InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Produced");
                    linkedList.add(1);
                    notify();
                    try {
                        Thread.sleep(1000);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread consume = new Thread(new Runnable() {
            @Override
            public void run() {
                // produce
                synchronized(this) {
                    while (linkedList.isEmpty()) {
                        try {
                            wait();
                        } catch(InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("Consumed");
                    linkedList.removeFirst();
                    notify();
                    try {
                        Thread.sleep(1000);
                    } catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        producer.start();
        consume.start();
        producer.join();
        consume.join();

    }
}

我們得到的輸出為:

然后程序掛起。

請提供可能的解決方案/解釋的幫助

使用共享鎖。 在發布的代碼中,每個Runnable都將自身用作鎖定,因此不會發生實際的鎖定。

當一個線程等待時,另一個線程需要在同一鎖上調用notify才能喚醒等待的線程。 從您的日志記錄中我們知道Producer線程會執行其操作,但是由於notify所作用的鎖與Consumer使用的鎖不同,因此Consumer線程永遠不會喚醒。

更改代碼以使用共享鎖的工作原理是:

import java.util.*;

public class ProducerConsumer { private static LinkedList linkedList = new LinkedList();

public static void main(String a[]) throws InterruptedException {
    final Object lock = new Object();
    Thread producer = new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (lock) {
                while (linkedList.size() ==1) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Produced");
                linkedList.add(1);
                lock.notify();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    Thread consume = new Thread(new Runnable() {
        @Override
        public void run() {
            // produce
            synchronized (lock) {
                while (linkedList.isEmpty()) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Consumed");
                linkedList.removeFirst();
                lock.notify();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    producer.start();
    consume.start();
    producer.join();
    consume.join();

}

}

輸出為:

c:\example>java ProducerConsumer
Produced
Consumed

我想這就是您的期望。

順便說一句, 看到了為隊列的簡單實現而寫的另一個答案 與將代碼放在訪問數據結構的線程中相比,保護共享數據結構更好,尤其要看代碼編寫起來有多容易。

並發意味着您無法在運行時之前知道哪個線程將首先結束。 因此,您不知道哪個消費者和生產者首先啟動,執行或完成。

為了幫助您,您可以使用循環屏障https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html或應用Fork / Join Framework https:// docs。 oracle.com/javase/tutorial/essential/concurrency/forkjoin.html

您的同步塊只是說:一次只能有一個線程可以執行這部分代碼,而不能執行第一個和第二個之后的代碼。

CyclicBarrier的工作方式示例:

service = Executors.newFixedThreadPool(numThreadsTotal);
CyclicBarrier c = new CyclicBarrier(numThreadsToWait);
runProducer();
c.await();
runConsumer();

它將等待,直到有與numThreadsT​​oWait一樣多的已執行runProducer的線程來執行runConsumer()。

也許使用大小為1的線程池可以幫助您,但是您將失去並發的好處。

我認為您能做的最好是使用BlockingQueue

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM