首页 > 解决方案 > 使用单独的线程更新全局 Hashmap 时出现 Nullpointer 异常

问题描述

我的 Hashmap 中出现 Null 指针异常,它是一个全局变量,并由单独的线程更新。虽然调试所有线程都运行良好并更新 Hashmap 但是当我执行程序时,一个线程会抛出 Null 指针。

public class RunEngine {

	/**
	 * @param args
	 */

	public static Map<Integer, List<Event>> queue_map = new HashMap<Integer, List<Event>>();

	public static void main(String[] args) {


		/*
		 * check file present in data set folder or not , if present fetch the file name
		 * and store then in a list
		 */
		File folder = new File(ConfigurationParameters.FOLDER_PATH);
		File[] listOfFiles = folder.listFiles();

		/* Create a thread pool for number of Publishers which you want to initialise */
		ThreadPoolExecutor executor = new ThreadPoolExecutor(ConfigurationParameters.P_NUMBER,
				ConfigurationParameters.P_NUMBER + 3, 100, TimeUnit.MILLISECONDS,
				new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.CallerRunsPolicy());   // P_NUMBER = 2

		for (int i = 1; i <= ConfigurationParameters.P_NUMBER; i++) {

			ConnectP conn = new ConnectP(
					ConfigurationParameters.FOLDER_PATH + "\\" + listOfFiles[i - 1].getName(),
					ConfigurationParameters.DISTRIBUTION_RANDOM, i);
			executor.execute(conn);
		}
		executor.shutdown();

		

	}

}

ConnectP 类如下所示:

public class ConnectP implements Runnable {

    private String file_path;
    private String distribution_constant;
    private int pub_id;

    /**
     * @param file_path
     * @param distribution_constant
     */
    public ConnectP(String file_path, String distribution_constant, int pub_id) {
        super();
        this.file_path = file_path;
        this.distribution_constant = distribution_constant;
        this.pub_id = pub_id;
    }

    public void streamevent(List<Event> eventlist, String distributionconstant)
            throws InterruptedException {


        if (distributionconstant.matches(ConfigurationParameters.PUBLISHER_DISTRIBUTION_RANDOM)) {

            List<Event> publisherqueue = new ArrayList<>();
            RunEngine.queue_map.put(pub_id, publisherqueue);
            for (Event event : eventlist) {
                long start = System.currentTimeMillis(); // get present system timestamp
                event.setTimeStamp(start);
                publisherqueue.add(event); // add to publisher queue
                Random rand = new Random();
                int value = rand.nextInt(1000); // add a random seed for streaming
                Thread.sleep(value); // sleep the process for that value
                System.out.println("Publisher " + String.valueOf(pub_id) + " : " + RunEngine.queue_map.get(pub_id).size()); **(throwing exception line 68)**


            }

        }


    }
    // function to connect to source file and return list of publication event
    public List<Event> connectsource(String filepath) {
        // return list after reading file
        return list;
    }


    @Override
    public void run() {
        // TODO Auto-generated method stub

        List<Event> event = this.connectsource(file_path); // connect to source
        try {
            System.out.println("THREAD " + String.valueOf(pub_id)+ " Initiated");
            this.streamevent(event, distribution_constant);**(throwing exception line 121)**
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

}

我得到的例外是

Exception in thread "pool-1-thread-1" java.lang.NullPointerException
	at org.test.event.ConnectP.streamevent(ConnectP.java:68)
	at org.test.event.ConnectP.run(ConnectP.java:121)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at java.lang.Thread.run(Unknown Source)

我会感谢您的帮助。如果有一些有效的方法可以做到这一点,将不胜感激。

标签: javamultithreadingnullpointerexceptionglobal-variables

解决方案


I am getting a Null pointer exception in my Hashmap which is a global variable and is getting updated by separate threads.

HashMap is not a synchronized class. If multiple threads are updating the map at the same time then it is very possible that their memory view of this map will get out of sync which could cause NPEs, infinite loops, or other bad things.

<tldr> The easiest way to do this is to use the ConcurrentHashMap class which takes care of synchronization for you. </tldr>

// thread-1   
RunEngine.queue_map.put(pub_id, publisherqueue);
...
// thread-2
RunEngine.queue_map.get(pub_id).size()

Right. One thread looks to be putting and another is getting. When you do this in multiple threads then you need to worry about:

  • Memory visibility to ensure the changes to the map in one thread are seen by other threads.
  • Locking to ensure that two threads aren't making changes to the map at the same time and their updates clash.

Basically each thread has a local memory cache per-processor that allows it to access cached results efficiently without having to get everything from main memory. So when thread-1 updates the map, it may only do so in it's cache. If thread-2 then gets from the map it might see none of of the updates or, worse, a partial update of the map depending on the virtual memory details. If both threads are updating the map then the one that flushes its memory cache to main memory will overwrite the updates made by the other one.

This is true with any object that you are sharing between threads. Only immutable objects with final fields can be shared between threads without worry. Otherwise you need to use the volatile or synchronized keywords to ensure they are shared appropriately. To do this correctly is pretty difficult and requires a lot of experience with Java threads and memory synchronization constructs.

The Java tutorial about synchronization may be a good place to get started.


推荐阅读