寻找java并发模型的建议

时间:2014-05-30 17:09:46

标签: java multithreading concurrency

我正在使用AMQP对进行图搜索的多个线程进行排队。除了通过单独的线程定期间隔之外,不会修改图形。什么是最好的并发模型,等待所有活动搜索完成,阻止这些线程,并允许更新线程在解锁搜索线程之前修改图形?

我一直在阅读http://docs.oracle.com/javase/tutorial/essential/concurrency/,但我似乎找不到任何完全适合我模特的东西。

有什么建议吗?

谢谢!

编辑:我正在使用ExecutorService来处理线程。

2 个答案:

答案 0 :(得分:1)

你真的需要阻止吗?也许非阻塞的写时复制就足够了。

更新线程应该克隆当前图形结构并在克隆上应用更新。完成更新后,应宣布新图表 - 只需覆盖共享参考。

搜索线程应该将图形引用保存到局部变量或范围一次并使用它。永远不会修改共享图,因此不需要任何锁定和等待。

优点:

  • 没有锁定和等待读者,
  • 如果更新失败,读者仍然使用旧结构
  • 适用于长时间运行和偶尔更新,其中读取次数多于更新次数
  • 垃圾收集器处理旧图

缺点:

  • 如果某些读者在更新之前就开始操作,他们可能会对旧数据进行操作 - 这可以通过检查图表的原始引用是否发生更改并最终重新启动整个操作来解决。
  • 多个编写者可能会在图中引入冲突,但可以通过几种冲突解决技术解决,最简单的方法是忽略以前的更改并覆盖(“Take mine”)。

也可以将写时复制仅应用于图形的一部分。特别是如果图形是内存消耗结构。然而,这是一个非常难的主题 - 请参阅MVCCSTM (Software Transactional Memory)

答案 1 :(得分:0)

我不熟悉AMQP,但这是一个生产者/消费者问题,所以有几种方法可以用Java来解决这个问题。对于Futures和ReentrantLock来说,这是一个非常快速和肮脏的例子:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo {
    private static Random rand = new Random();
    private static final Lock lock = new ReentrantLock();
    private static boolean updating = false;

    private static List<Future<Integer>> futureList = new ArrayList<Future<Integer>>();
    private static ExecutorService pool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    private static Callable<Integer> callable = new Callable<Integer>() {
        @Override
        public Integer call() {
            return rand.nextInt();
        }
    };

    private static void doUpdate() {
        if (lock.tryLock()) {
            updating = true;
            try {
                for (Future<Integer> future : futureList) {
                    System.out.println(future.get());
                }
                futureList.clear();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println();
                lock.unlock();
                updating = false;
            }
        }
    }

    public static void main(String[] args) throws Exception {
        // submitter thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                int submitCount = 0;
                while (submitCount < 10) {
                    if (!updating) {
                        futureList.add(pool.submit(callable));
                        submitCount++;
                    }

                    try {
                        Thread.sleep(1000); // arbitrary
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        // update thread
        new Thread(new Runnable() {
            @Override
            public void run() {
                int updateCount = 0;
                while (updateCount < 5) {
                    doUpdate();
                    updateCount++;
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

我将更新线程设置为提交线程频率的一半。因此,如果你运行它,你会看到更新器每次运行时剥离两个整数。提交线程必须等到更新程序释放锁定。

还有其他方法 - 查看BlockingQueue界面:您可能想要试验。