Java中用于ConcurrentHashMap的多个线程

时间:2018-10-01 22:01:53

标签: java multithreading

我做了两个线程,一个是获取数据,另一个是保存数据。 我的问题是,在存储从Thread1读取的数据的过程中没有处理该问题。

我想提取1,000,000个元素并将其创建为文件。元素大小很大,所以我将元素大小除以100,000。然后,循环将运行10次。一个线程从另一台服务器读取100,000个数据。另一个线程从第一个线程获取数据并将其写入文件。

我的原始情况如下:

第一个线程读取总键,值大小。 这将是100,000〜1,000,000。我假设我将处理1,000,000数据。然后Count设置1,000,000。第一个线程除以100,000,然后从服务器读取100,000。然后,第一个线程调用setData(Key,Value map)。 它将循环十次。

第二个线程将循环10次。首先,通过调用getMap()方法获取数据。并调用writeSeq(hashmap)方法。它将数据写入写程序流。它还没有冲洗。这里有问题。它通过调用getMap()成功获取数据大小。但是,writeSeq方法无法处理所有大小的值。当我得到100,000的大小时,它作为随机处理。分别是100、1500、0、8203 ...

第一个线程在下面:

public void run() {
        getValueCount(); //initialize value.

        while (this.jobFlag) {
            getSortedMap(this.count); //count starts the number of all elements size.
//For example, Total size is 1,000,000. Then count will sets a 1,000,000 and it is decreased as 100,000.
// Also setMap() is called in this method.
            if (!jobFlag) //If all processing is done, jobFlag is set as false.
                break;
        }

        resetValue();
    }

第二个线程在下面:

public void run() {
        setWriter(); //Writer Stream creates;

        double count  = 10; //the number of loop. 

        ConcurrentHashMap<String, String> hash = new ConcurrentHashMap<String,String>();

        for (int i = 0; i <= count - 1; i++) {
            hash = share.getMap();
            writeSeq(hash);
        }

        closeWriter(); //close Writer stream
    }

这是共享源:

import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

public class ShareData {

    ConcurrentHashMap<String, String> map;

    public synchronized ConcurrentHashMap<String, String> getMap(){
        if (this.map == null) {
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        ConcurrentHashMap<String, String> hashmap = map;

        this.map = null;

        return hashmap;
    }

    public synchronized void setMap(ConcurrentHashMap<String, String> KV) {
        if (this.map != null) {
            try {
                wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        this.map = KV;
        notify();
    }

}

在那之后,第二个保存数据的线程被盯着。 KV的大小很好,但是在进行foreach时不会处理所有值。此外,每次创建文件时,大小都是不同的。是同步的问题吗?

public synchronized void writeSeq(ConcurrentHashMap<String, String> KV) {

        AtomicInteger a = new AtomicInteger(0);
        System.out.println(KV.size()); //ex) 65300
        redisKV.entrySet().parallelStream().forEach(
                entry -> { 
                    try {
                        a.incrementAndGet();
                        writer.append(new Text(entry.getKey()), new Text(entry.getValue()));
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                });
        System.out.println(a.get()); //ex) 1300
        i = 0;
        notify();

    }

3 个答案:

答案 0 :(得分:1)

i++;不是thread-safe。您得到的数量将少于更新的数量。改用AtomicInteger及其incrementAndGet()方法。

答案 1 :(得分:1)

更好的方法是使用BlockingQueue,一个线程放入队列,另一个线程从队列中取出。

答案 2 :(得分:1)

  

KV的大小合适,但是在处理foreach时不会处理所有值。另外,每次创建文件时,大小都不同。是同步的问题吗?

不清楚。我可以看到一个小问题,但这不太可能导致您描述的问题。

  • if (map == null) wait();代码应为while循环。

  • if (map != null) wait();代码应为while循环。

问题在于,如果一个线程收到虚假通知,则它可能会以错误状态继续map。您需要重试测试。 (如果您阅读Object的Javadoc,则会看到一个示例,该示例正确实现了条件变量。)

除此之外,您的问题的根本原因似乎不在您显示给我们的代码中。


但是,如果我要猜测,我的猜测是一个线程正在添加或删除ConcurrentHashMap中的条目,而第二个线程正在处理它 1 。您向我们展示的getMap / setMap方法必须被适当地使用(即在适当的位置使用适当的参数调用),以避免两个线程相互干扰。您尚未向我们显示该代码。

因此,如果我的猜测正确,那么您的问题是逻辑错误而不是低级同步问题。但是,如果您需要更好的答案,则需要编写和发布适当的MCVE。


1-ConcurrentHashMap的迭代器弱一致。这意味着,如果您在迭代时更新地图,则可能会错过迭代中的条目,或者可能多次看到它们。