簡體   English   中英

使用 Java(同步)但始終運行相同線程的生產者/消費者模型

[英]Producer/Consumer Model using Java(Synchronized) but always run the same thread

一切看起來都很好,但是當我運行時,結果如下:

開始線程(1):Pro2

開始線程(2):Pro1

啟動線程(1):Con1

開始線程(2):Con2

Pro2 制作:EWlmJdi2KK

剩下:

Pro2:EWlmJdi2KK

---左:1---

Con2 消耗:EWlmJdi2KK

剩下:

---左:0---

Pro2 產品:Nx7QPyG7vs

剩下:

Pro2:Nx7QPyG7vs

---左:1---

Pro2 生產:xl85Zwr80a

左:Pro2:Nx7QPyG7vs Pro2:xl85Zwr80a

---左:2---

在運行時間內始終是相同的生產者和消費者。

這是我的代碼:Main.java:

package com.producer;

import org.apache.commons.lang3.RandomStringUtils;

public class Main {

    public static void main(String[] args) {
        ThreadSynchronized semaphore = new ThreadSynchronized();
        ThreadSynchronized.Producer pro = semaphore.new Producer("1");
        ThreadSynchronized.Consumer con = semaphore.new Consumer("2");
        new Thread(pro, "Pro2").start();
        new Thread(pro, "Pro1").start();
        new Thread(con, "Con1").start();
        new Thread(con, "Con2").start();

    }
}

線程同步.java:

package com.producer;

import java.util.LinkedList;

/**
 * Created by SkyAo on 15/10/18.
 */
public class ThreadSynchronized {
    private static int pid;
    private static int cid;
    public LinkedList<Item> items = new LinkedList<>();
    private Item temp;
    private int item;
    private boolean flag = false;

    class Producer implements Runnable {
        private String name;
        private int id;

        public Producer(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            this.id = ++pid;
            System.out.println("Start Thread(" + this.id + "): " + Thread.currentThread().getName());
            while (true)
                this.produce();
        }

        private synchronized void produce() {
            try {

                Thread.sleep((int)(Math.random()*5000)+3000);
                if (items.size() < 5) {
                    items.add(new Item(this.id, Thread.currentThread().getName()));
                    temp = items.getLast();
                    System.out.println(temp.sourceName + " Produce: " + temp.semi);

                    System.out.println("Left: ");

                    for (Item item : items) {
                        System.out.println(item.sourceName + ":" + item.semi);
                    }

                    System.out.println("---Left: " + items.size() + "---");
                } else {
                    super.wait();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                super.notifyAll();
            }

        }


    }

    class Consumer implements Runnable {
        private String name;
        private int id;

        public Consumer(String name) {
            this.name = name;
            //this.id = ++cid;
        }

        @Override
        public void run() {
            this.id = ++cid;
            System.out.println("Start Thread(" + this.id + "): " + Thread.currentThread().getName());
            while (true)
                this.consume();

        }

        private synchronized void consume() {
            try {
                Thread.sleep((int) (Math.random() * 5000) + 3000);
                if (items.size() > 0) {
                    temp = items.removeFirst();
                    System.out.println(Thread.currentThread().getName() + " Consume: " + temp.semi);
                    System.out.println("Left: ");

                    for (Item item : items) {
                        System.out.println(item.sourceName + ":" + item.semi);
                    }

                    System.out.println("---Left: " + items.size() + "---");
                } else {
                    super.wait();
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                super.notifyAll();
            }
        }
    }
}

我不知道發生了什么以及如何解決它,謝謝您的幫助。

使用通知/等待解決生產者/消費者問題很棘手。 我的建議是你研究“隊列”。 更具體地說,是AbstractQueue的子類之一。

您將首先創建一個合適的隊列。 然后創建 Producer 對象。 每個生產者對象都實現了Runnable 創建對象時,您將隊列作為參數傳遞。 您將生產者對象作為新線程啟動。

你對 Consumer 對象做同樣的事情。 它們還實現了Runnable並在創建時作為參數傳遞給隊列。 當所有生產者和消費者線程都在運行時,您的主程序就完成了。

它是傳遞給所有生產者和消費者線程的同一個隊列對象。 同步由隊列處理。

Producer 對象中的代碼向隊列寫入或放置條目——而 Consumers 中的代碼從隊列中讀取或提取條目。

有關示例, 請參閱此博客

您以一種不同尋常的方式構建了您的解決方案。 通常,生產者和消費者本身並不同步,但對他們共享的資源的訪問是同步的。

考慮這個例子。 這里, MyQueue一個實例是共享資源——它是具有同步方法的東西。 生產者和消費者本身並不同步。

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

class MyQueue {
    private int capacity;
    private List<Integer> queue = new LinkedList<>();

    public MyQueue(int capacity) {
        this.capacity = capacity;
    }

    public synchronized void enqueue(int item) throws InterruptedException {
        while (queue.size() == this.capacity) {
            wait();
        }

        System.out.println("Thread " + Thread.currentThread().getName() +
                           " producing " + item);
        queue.add(item);

        if (queue.size() == 1) {
            notifyAll();
        }
    }

    public synchronized int dequeue() throws InterruptedException {
        int item;

        while (queue.size() == 0) {
            wait();
        }

        item = queue.remove(0);

        System.out.println("Thread " + Thread.currentThread().getName() +
                           " consuming " + item);

        if (queue.size() == (capacity - 1)) {
            notifyAll();
        }

        return item;
    }
}

public class ProducerConsumer {
    private static class Producer implements Runnable {
        private MyQueue queue;
        private Random random = new Random();

        public Producer(MyQueue queue) {
            this.queue = queue;
        }

        public void run() {
            try {
                for (;;) {
                    queue.enqueue(random.nextInt());
                    Thread.sleep((int)(Math.random() * 3000) + 1000);
                }
            } catch (InterruptedException ex) {
                System.out.println(Thread.currentThread().getName() +
                        " interrupted");
            }
        }
    }

    private static class Consumer implements Runnable {
        private MyQueue queue;

        public Consumer(MyQueue queue) {
            this.queue = queue;
        }

        public void run() {
            try {
                for (;;) {
                    queue.dequeue();
                    Thread.sleep((int)(Math.random() * 5000) + 3000);
                }
            } catch (InterruptedException ex) {
                System.out.println(Thread.currentThread().getName() +
                        " interrupted");
            }
        }
    }

    public static void main(String[] args) {
        MyQueue queue = new MyQueue(10);

        new Thread(new Producer(queue), "Producer 1").start();
        new Thread(new Producer(queue), "Producer 2").start();
        new Thread(new Consumer(queue), "Consumer 1").start();
        new Thread(new Consumer(queue), "Consumer 2").start();
    }
}

示例輸出:

$ java ProducerConsumer
Thread Producer 1 producing 1380029295
Thread Consumer 1 consuming 1380029295
Thread Producer 2 producing 1449212482
Thread Consumer 2 consuming 1449212482
Thread Producer 2 producing -1845586946
Thread Producer 1 producing -1072820603
Thread Producer 1 producing 1224861300
Thread Producer 2 producing 507431251
Thread Consumer 2 consuming -1845586946
Thread Consumer 1 consuming -1072820603
Thread Producer 2 producing -1305630628
Thread Producer 1 producing 1413011254
Thread Producer 2 producing -222621018
Thread Consumer 2 consuming 1224861300
Thread Producer 1 producing -1628544536
Thread Consumer 1 consuming 507431251

您正在以 Java vanilla 方式解決棘手的設計問題。 這種方法容易出錯且不可靠。 我建議改用現成的解決方案。 不過,對於你的情況,我認為你會發現這篇文章很有幫助。

暫無
暫無

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

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