用java多线程回调

时间:2013-10-28 17:39:44

标签: java multithreading

我正在尝试多线程导入作业,但遇到导致重复数据的问题。我需要将我的地图保留在循环之外,这样我的所有线程都可以更新并从中读取,但是如果没有它是最终的我就不能这样做,并且最终我无法更新地图。目前我需要将我的Map对象放在run方法中,但是当值最初不在数据库中并且每个线程创建一个新的时,问题就出现了。这会导致数据库中出现重复数据。有人知道如何做一些回电来更新我的地图吗?

ExecutorService executorService = Executors.newFixedThreadPool(10);

final Map<Integer, Object> map = new HashMap<>();
map.putAll(populate from database);
for (int i = 0; i < 10; i++) {

    executorService.execute(new Runnable() {
        public void run() {

        while ((line = br.readLine()) != null) {
            if(map.containsKey(123)) {
                //read map object
                session.update(object);                
            } else {
                map.put(123,someObject);
                session.save(object);
            }            

            if(rowCount % 250 == 0)
            tx.commit;
        });

}

executorService.shutdown();

4 个答案:

答案 0 :(得分:1)

您需要使用一些同步技术。

有问题的部分是当不同的线程试图将一些数据放入地图时。

示例:

线程1正在检查地图中是否存在具有键123的对象。在线程1添加新对象到映射之前,执行线程2。线程2还检查是否存在具有键123的对象。然后两个线程都将对象123添加到映射。这会导致重复......

您可以在此处详细了解同步

http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

答案 1 :(得分:1)

根据您的问题说明,您似乎想要一张数据一致的地图,并且您始终拥有最新的最新数据,而不会错过任何更新。

在这种情况下,您可以将地图映射为Collections.synchronizedMap()。这将确保对地图的所有读写更新都进行同步,因此您可以保证使用地图中的最新数据查找密钥,并保证只对地图进行写入。

请参阅this SO讨论,了解与地图一起使用的并发技术之间的区别。

另外,还有一件事 - 将地图定义为最终意味着你无法修改地图 - 你绝对可以在地图中添加和删除元素。但是,您无法做的是将变量更改为指向另一个地图。下面的简单代码片段说明了这一点:

    private final Map<Integer, String> testMap = Collections.synchronizedMap(new HashMap<Integer,String>());
    testMap.add(1,"Tom"); //OK
    testMap.remove(1);   //OK
    testMap = new HashMap<Integer,String>(); //ERROR!! Cannot modify a variable with the final modifier

答案 2 :(得分:1)

我会建议以下解决方案

  • 使用ConcurrentHashmap
  • 请勿在抓取线程中使用updatecommit
  • 当您的地图在单独的帖子中达到临界尺寸时触发savecommit

伪码样本:

final Object lock = new Object();

...

executorService.execute(new Runnable() {
    public void run() {
        ...
        synchronized(lock){
            if(concurrentMap.size() > 250){
               saveInASeparateThread(concurrentMap.values().removeAll()));          
            }
        }
    }
}

答案 3 :(得分:1)

以下逻辑解决了我的问题。以下代码未经过测试。

ExecutorService executorService = Executors.newFixedThreadPool(10);

final Map<Integer, Object> map = new ConcurrentHashMap<>();
map.putAll(myObjectList);

List<Future> futures = new ArrayList<>();

for (int i = 0; i < 10; i++) {
    final thread = i;

    Future future = executorService.submit(new Callable() {
        public void call() {

        List<MyObject> list;

        CSVReader reader = new CSVReader(new InputStreamReader(csvFile.getStream()));

        list = bean.parse(strategy, reader);

        int listSize = list.size();
        int rowCount = 0;

        for(MyObject myObject : list) {

            rowCount++;

            Integer key = myObject.getId();

            if(map.putIfAbsent(key, myObject) == null) {
                session.save(object);                
            } else {
                myObject = map.get(key);
                //Do something
                session.update(myObject);
            }            

            if(rowCount % 250 == 0 || rowCount == listSize) {
                tx.flush();
                tx.clear();
            }
        };

        tx.commit();

        return "Thread " + thread + " completed."; 

    });  

    futures.add(future);  
}

for(Future future : futures) {
    System.out.println(future.get());
}

executorService.shutdown();