【问题标题】:NullPointerException in multi-threading simulation with HashMap使用 HashMap 进行多线程模拟中的 NullPointerException
【发布时间】:2020-07-27 19:55:39
【问题描述】:

我正在尝试做一个多线程模拟器,其中有工人(线程)和要解决的工作,所以每个线程都必须解决一个工作并开始解决下一个 按顺序,作业的整数是解决作业所需的时间(以秒为单位),这是一个模拟,因此代码打印线程的索引 作业的初始化时间,但它没有休眠那么秒数。

问题是,只有当有很多具有相同编号的作业时,我才会收到 NullPointerException,例如 4 12(4 个线程用于 12 个作业) 1 1 1 1 1 1 1 1 1 1 1 1 (需要 1 秒才能完成的 12 个作业)它在这部分启动异常:

if (workersReady.size()>1) { 
              bestWorker = workersReady.iterator().next();
              workersReady.remove(bestWorker);
              workersReadyAtTimeT.remove(currentTime);
              workersReadyAtTimeT.put(currentTime,workersReady);
              nextTimesQueue.add(currentTime);

输入必须是这样的:

第一行: 2 5 表示 5 个作业有两个线程(worker)

按回车键并写入第二行: 1 2 3 4 5 这是一个整数的作业,这意味着处理该作业的时间成本,因此按 Enter 后的输出将是这样的:

0 0 两个线程试图同时从列表中获取作业,所以实际上索引为 0 的线程 接受第一份工作并在 0 时开始工作

1 0 索引为 1 的线程接受第一个作业并在 0 时刻开始处理它

0 1 1 秒后,线程 0 完成第一个作业并从列表中获取第三个作业,并且 在时间 1 立即开始处理它。

1 2 一秒钟后,线程 1 完成了第二个作业并从列表中取出第四个作业,并在时间 2 立即开始处理它

0 4 最后,又过了 2 秒,线程 0 完成了第三个作业并从列表中取出第五个作业,并在时间 4 立即开始处理它

这是代码:

import java.io.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;
import java.util.StringTokenizer;

public class JobQueue {
    private int numWorkers;
    private int[] jobs;
    private int[] assignedWorker;
    private long[] startTime;

    private FastScanner in;
    private PrintWriter out;

    public static void main(String[] args) throws IOException {
        new JobQueue().solve();
    }

    private void readData() throws IOException {
        numWorkers = in.nextInt();
        int m = in.nextInt();
        jobs = new int[m];
        for (int i = 0; i < m; ++i) {
            jobs[i] = in.nextInt(); 
        }
    }

    private void writeResponse() {
        for (int i = 0; i < jobs.length; ++i) {
            out.println(assignedWorker[i] + " " + startTime[i]);
        }
    }

    private void assignJobs() {
        // TODO: replace this code with a faster algorithm.
        assignedWorker = new int[jobs.length];
         startTime = new long[jobs.length];
         PriorityQueue<Integer> nextTimesQueue = new PriorityQueue<Integer>();
         HashMap<Integer, Set<Integer>> workersReadyAtTimeT = new HashMap<Integer,Set<Integer>>();
         long[] nextFreeTime = new long[numWorkers];
         int duration = 0;
         int bestWorker = 0;
         for (int i = 0; i < jobs.length; i++) {
          duration = jobs[i];
          if(i<numWorkers) {
            bestWorker = i;
            nextTimesQueue.add(duration);
            addToSet(workersReadyAtTimeT, duration, i,0);
          }else {
            int currentTime = nextTimesQueue.poll();
            Set<Integer> workersReady = workersReadyAtTimeT.get(currentTime);
            if (workersReady.size()>1) { 
              bestWorker = workersReady.iterator().next();
              workersReady.remove(bestWorker);
              workersReadyAtTimeT.remove(currentTime);
              workersReadyAtTimeT.put(currentTime,workersReady);
              nextTimesQueue.add(currentTime);
            } else {
              bestWorker = workersReady.iterator().next();
              workersReadyAtTimeT.remove(currentTime);
              nextTimesQueue.add(currentTime+duration);
              addToSet(workersReadyAtTimeT, duration, bestWorker, currentTime);
            }
          }
          
          assignedWorker[i] = bestWorker;
          startTime[i] = nextFreeTime[bestWorker];
          nextFreeTime[bestWorker] += duration;
         }
        }
    
    private void addToSet(HashMap<Integer, Set<Integer>> workersReadyAtTimeT, int duration, int worker, int current) {
        if(workersReadyAtTimeT.get(current+duration)==null) {
          HashSet<Integer> s = new HashSet<Integer>();
          s.add(worker);
          workersReadyAtTimeT.put(current+duration, s);
        }else {
          Set<Integer> s = workersReadyAtTimeT.get(current+duration);
          s.add(worker);
          workersReadyAtTimeT.put(current+duration,s);
         }
        }

    public void solve() throws IOException {
        in = new FastScanner();
        out = new PrintWriter(new BufferedOutputStream(System.out));
        readData();
        assignJobs();
        writeResponse();
        out.close();
    }

    static class FastScanner {
        private BufferedReader reader;
        private StringTokenizer tokenizer;

        public FastScanner() {
            reader = new BufferedReader(new InputStreamReader(System.in));
            tokenizer = null;
        }

        public String next() throws IOException {
            while (tokenizer == null || !tokenizer.hasMoreTokens()) {
                tokenizer = new StringTokenizer(reader.readLine());
            }
            return tokenizer.nextToken();
        }

        public int nextInt() throws IOException {
            return Integer.parseInt(next());
        }
    }
}
 

编辑:我使用了 ConcurentHashMap 并仍在启动 NullPointer

【问题讨论】:

    标签: java multithreading algorithm hashmap queue


    【解决方案1】:

    HashMap 不是线程安全的。

    如果您在没有“外部”同步的情况下与来自多个线程的 hashmap 进行交互,那么 HashMap 的规范表明任何事情都是公平的游戏。如果您的计算机开始播放 Yankee Doodle Dandee,这将与规范兼容,并且不会接受该帐户的错误报告。

    换句话说,您必须自己处理。

    通常,正确的做法是改用ConcurrentHashMap(来自非常有用的java.util.concurrent 包),所以它就在这里。

    如果必须,您也可以进行外部同步。例如:

    synchronized (workersReady) {
        // interact with workersReady here
    }
    

    但是synchronized 在这里使用起来很笨拙,很可能会消除尝试多线程处理这些东西的大部分/所有好处。

    请注意,“工人池”听起来更像是一份工作,例如ExecutorPool。请务必检查 j.u.c 包,我很确定它有更合适的内容,因此您可以删除您编写的大部分内容并使用经过仔细调整、预先测试和优化的解决方案。

    【讨论】:

    • 我开始学习数据结构,但我不知道如何实现,你能帮我吗?有没有更快的方法来做我正在尝试的事情执行速度是我的首要任务
    • 使用ConcurrentLinkedQueue - 让线程从中拉出作业。
    • 仍在启动 NullPointer
    • 然后使用基于 yoru CLQ 的代码发布一个新问题;更新这个似乎不合适,没有代码我帮不了你。
    【解决方案2】:

    也许看看 ConcurrentHashMap。

    【讨论】:

    • 仍在启动 NullPointer
    猜你喜欢
    • 2020-08-03
    • 1970-01-01
    • 1970-01-01
    • 2011-06-16
    • 2018-11-21
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多