简体   繁体   English

如何在多个线程中使用相同的hashmap

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

I have a Hashmap that is created for each "mailer" class and each "agent" class creates a mailer.我有一个为每个“邮件程序”类创建的 Hashmap,每个“代理”类创建一个邮件程序。 My problem is that each of my "agents" creates a "mailer" that in turn creates a new hashmap.我的问题是我的每个“代理”都创建了一个“邮件程序”,而后者又会创建一个新的哈希图。 What I'm trying to do is to create one Hashmap that will be used by all the agents(every agent is a thread).我想要做的是创建一个将由所有代理使用的 Hashmap(每个代理都是一个线程)。

This is the Agent class:这是代理类:

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);
    }
}

This is the Mailer class :这是 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;
       }
   }

}

I have tried so far : Creating the mailer object inside the run method in agent.到目前为止,我已经尝试过:在代理的 run 方法中创建邮件程序对象。

Going by the idea (based on your own answer to this question) that you made the map static, you've made 2 mistakes.根据您将地图设为静态的想法(基于您自己对这个问题的回答),您犯了 2 个错误。

do not use static不要使用静态

static means there is one map for the entire JVM you run this on. static意味着您运行它的整个 JVM 有一个映射。 This is not actually a good thing: Now you can't create separate mailers on one JVM in the future, and you've made it hard to test stuff.这实际上不是一件好事:现在您将来无法在一个 JVM 上创建单独的邮件程序,并且您很难测试内容。

You want something else: A way to group a bunch of mailer threads together (these are all mailers for the agent), but a bit more discerning than a simple: "ALL mailers in the ENTIRE system are all the one mailer for the one agent that will ever run".你想要别的东西:一种将一堆邮件程序线程组合在一起的方法(这些都是代理的邮件程序),但比简单的更有辨别力:“整个系统中的所有邮件程序都是一个代理程序的一个邮件程序那将永远运行”。

A trivial way to do this is to pass the map in as argument.一种简单的方法是将地图作为参数传递。 Alternatively, have the map be part of the agent, and pass the agent to the mailer constructor, and have the mailer ask the agent for the map every time.或者,让地图成为代理的一部分,并将代理传递给邮件程序构造函数,让邮件程序每次都向代理询问地图。

this is not thread safe这不是线程安全的

Thread safety is a crucial concept to get right, because the failure mode if you get it wrong is extremely annoying: It may or may not work, and the JVM is free to base whether it'll work right this moment or won't work on the phase of the moon or the flip of a coin: The JVM is given room to do whatever it feels like it needs to, in order to have a JVM that can make full use of the CPU's powers regardless of which CPU and operating system your app is running on.线程安全是一个正确的关键概念,因为如果你弄错了失败模式是非常烦人的:它可能会或可能不会工作,并且 JVM 可以自由地判断它此时是否会正常工作或不会工作在月相或硬币抛掷时:JVM 有空间去做它认为需要做的事情,以便拥有一个 JVM 可以充分利用 CPU 的能力,而不管使用哪种 CPU 和操作系统您的应用程序正在运行。

Your code is not thread safe .您的代码不是线程安全的

In any given moment, if 2 threads are both referring to the same field, you've got a problem: You need to ensure that this is done 'safely', and the compiler nor the runtime will throw errors if you fail to do this, but you will get bizarre behaviour because the JVM is free to give you caches, refuse to synchronize things, make ghosts of data appear, and more.在任何给定的时刻,如果 2 个线程都引用同一个字段,那么您就会遇到问题:您需要确保这是“安全”完成的,如果您不这样做,编译器和运行时都将抛出错误,但你会得到奇怪的行为,因为 JVM 可以自由地为你提供缓存、拒绝同步事物、出现数据幽灵等等。

In this case the fix is near-trivial: Use java.util.concurrent.ConcurrentHashMap instead, that's all you'd have to do to make this safe.在这种情况下,修复几乎是微不足道的:改用java.util.concurrent.ConcurrentHashMap ,这就是确保安全所需的全部工作。

Whenever you're interacting with a field that doesn't have a convenient 'typesafe' type, or you're messing with the field itself (one thread assigns a new value to the field, another reads it - you don't do that here, there is just the one field that always points at the same map, but you're messing with the map) - you need to use synchronized and/or volatile and/or locks from the java.util.concurrent package and in general it gets very complicated.每当您与没有方便的“类型安全”类型的字段进行交互时,或者您正在弄乱字段本身时(一个线程为该字段分配一个新值,另一个线程读取它 - 您不会这样做在这里,只有一个字段始终指向同一张地图,但是您弄乱了地图)-您需要使用java.util.concurrent包中的synchronized和/或volatile和/或锁,并且通常情况下它变得非常复杂。 Concurrent programming is hard.并发编程很难。

我能够通过在代理类中将邮件程序更改为静态来解决这个问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何让多个线程使用相同的socket来读写? - How to make multiple threads use the same socket to read and write? 如何在处理多个线程时深层复制hashmap - How to deep copy a hashmap when working with multiple threads 多线程如何破坏Hashmap的内部结构 - How can multiple threads corrupt internal structure of Hashmap 多个线程同时使用同一个 JDBC 连接 - Concurrent use of same JDBC connection by multiple threads 使多个线程使用并更改相同的变量 - Make multiple threads use and change the same variable 多个线程使用相同的SimpleDateFormat而不使用ThreadLocal - Multiple threads use the same SimpleDateFormat without ThreadLocal 如何同步使用多个线程? - how to use multiple threads, in sync? 在两个线程中使用相同的Hashmap时,Hashmap中的并发修改异常 - Concurrent Modification Exception in Hashmap while using the same Hashmap in two threads 如何在Android中为多个线程使用多个按钮? - How to use multiple buttons for multiple threads in android? 如何在Java中使用多个线程迭代一个Collection,其中没有两个线程迭代在Collection的同一部分? - How to use multiple threads in Java to iterate over a Collection where no two threads ever iterate over the same part of the Collection?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM