繁体   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