簡體   English   中英

如何在多個線程中使用相同的hashmap

[英]How to use the same hashmap in multiple threads

我有一個為每個“郵件程序”類創建的 Hashmap,每個“代理”類創建一個郵件程序。 我的問題是我的每個“代理”都創建了一個“郵件程序”,而后者又會創建一個新的哈希圖。 我想要做的是創建一個將由所有代理使用的 Hashmap(每個代理都是一個線程)。

這是代理類:

public class Agent implements Runnable {

    private int id;
    private int n;
    private Mailer mailer;
    private static int counter;

    private List<Integer> received = new ArrayList<Integer>();

    @Override
    public void run() {
        System.out.println("Thread has started");
        n = 10;
        if (counter < n - 1) {
            this.id = ThreadLocalRandom.current().nextInt(0, n + 1);
            counter++;
        }
        Message m = new Message(this.id, this.id);
        this.mailer.getMap().put(this.id, new ArrayList<Message>());
        System.out.println(this.mailer.getMap());
        for (int i = 0; i < n; i++) {
            if (i == this.id) {
                continue;
            }
            this.mailer.send(i, m);
        }

        for (int i = 0; i < n; i++) {
            if (i == this.id) {
                continue;
            }
            if (this.mailer.getMap().get(i) == null) {
                continue;
            } else {
                this.received.add(this.mailer.readOne(this.id).getContent());
            }

        }
        System.out.println(this.id + "" + this.received);
    }
}

這是 Mailer 類:

public class Mailer {

   private HashMap<Integer, List<Message>> map = new HashMap<>();

   public void send(int receiver, Message m) {
       synchronized (map) {

           while (this.map.get(receiver) == null) {
               this.map.get(receiver);
           }
           if (this.map.get(receiver) == null) {

           } else {
               map.get(receiver).add(m);
           }
       }

   }

   public Message readOne(int receiver) {
       synchronized (map) {
           if (this.map.get(receiver) == null) {
               return null;
           } else if (this.map.get(receiver).size() == 0) {
               return null;
           } else {
               Message m = this.map.get(receiver).get(0);
               this.map.get(receiver).remove(0);
               return m;
           }
       }
   }

   public HashMap<Integer, List<Message>> getMap() {
       synchronized (map) {
           return map;
       }
   }

}

到目前為止,我已經嘗試過:在代理的 run 方法中創建郵件程序對象。

根據您將地圖設為靜態的想法(基於您自己對這個問題的回答),您犯了 2 個錯誤。

不要使用靜態

static意味着您運行它的整個 JVM 有一個映射。 這實際上不是一件好事:現在您將來無法在一個 JVM 上創建單獨的郵件程序,並且您很難測試內容。

你想要別的東西:一種將一堆郵件程序線程組合在一起的方法(這些都是代理的郵件程序),但比簡單的更有辨別力:“整個系統中的所有郵件程序都是一個代理程序的一個郵件程序那將永遠運行”。

一種簡單的方法是將地圖作為參數傳遞。 或者,讓地圖成為代理的一部分,並將代理傳遞給郵件程序構造函數,讓郵件程序每次都向代理詢問地圖。

這不是線程安全的

線程安全是一個正確的關鍵概念,因為如果你弄錯了失敗模式是非常煩人的:它可能會或可能不會工作,並且 JVM 可以自由地判斷它此時是否會正常工作或不會工作在月相或硬幣拋擲時:JVM 有空間去做它認為需要做的事情,以便擁有一個 JVM 可以充分利用 CPU 的能力,而不管使用哪種 CPU 和操作系統您的應用程序正在運行。

您的代碼不是線程安全的

在任何給定的時刻,如果 2 個線程都引用同一個字段,那么您就會遇到問題:您需要確保這是“安全”完成的,如果您不這樣做,編譯器和運行時都將拋出錯誤,但你會得到奇怪的行為,因為 JVM 可以自由地為你提供緩存、拒絕同步事物、出現數據幽靈等等。

在這種情況下,修復幾乎是微不足道的:改用java.util.concurrent.ConcurrentHashMap ,這就是確保安全所需的全部工作。

每當您與沒有方便的“類型安全”類型的字段進行交互時,或者您正在弄亂字段本身時(一個線程為該字段分配一個新值,另一個線程讀取它 - 您不會這樣做在這里,只有一個字段始終指向同一張地圖,但是您弄亂了地圖)-您需要使用java.util.concurrent包中的synchronized和/或volatile和/或鎖,並且通常情況下它變得非常復雜。 並發編程很難。

我能夠通過在代理類中將郵件程序更改為靜態來解決這個問題

暫無
暫無

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

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