Java 仅执行一次并发任务的模式

Java 仅执行一次并发任务的模式,java,design-patterns,concurrency,Java,Design Patterns,Concurrency,我在一个java服务器上工作,该服务器发送xmpp消息,工作人员从我的客户机执行任务 private static ExecutorService threadpool = Executors.newCachedThreadPool(); DispatchWorker worker = new DispatchWorker(connection, packet); threadpool.execute(worker); 很好,但我需要更多 我不想多次执行同一个请求 我的工作人员可以启动另一个

我在一个java服务器上工作,该服务器发送xmpp消息,工作人员从我的客户机执行任务

private static ExecutorService threadpool = Executors.newCachedThreadPool();

DispatchWorker worker = new DispatchWorker(connection, packet);
threadpool.execute(worker);
很好,但我需要更多

  • 我不想多次执行同一个请求
  • 我的工作人员可以启动另一个线程,该线程具有回退任务,并且一次只能运行一次。工作线程中的线程池
  • 我可以通过一个字符串来识别请求,也可以给回传任务一个id来识别它们

    我的解决方案是一个同步hashmap,其中我正在运行的任务使用它们的id注册。映射的引用将传递给工作线程,它们在完成时删除它们的条目

    觉得这个解决方案有点笨拙,所以我想知道是否有更优雅的模式/最佳实践


    非常感谢,m

    您可以使用单线程池或将线程池作为参数传递。(我将拥有游泳池
    final


    您可以使用HashSet来保护添加重复任务。

    我相信使用Map可以做到这一点。但是,除了同步的
    HashMap
    之外,您还可以使用
    ConcurrenHashMap
    ,它允许您指定并发级别,即有多少线程可以同时使用map。而且它还有原子的putIfAbsent操作。

    这正是它所做的(尽管它做的更多,比如将来调度作业)。

    我会使用队列和守护进程工作线程,它们总是在运行,并等待队列中有东西到达。通过这种方式,可以保证只有一个工人在处理一个请求。 如果只希望运行一个线程,请将POOLSIZE调低到1,或者使用newSingleThreadExecutor

    我不太理解您的第二个要求:您的意思是只允许1个线程作为后台任务运行吗?如果是这样,您可以创建另一个SingleThreadExecutor并将其用于后台任务。那么POOLSIZE>1就没有太大意义了,除非后台线程中完成的工作与工作线程本身中完成的工作相比非常短

    private static interface Request {};
    private final int POOLSIZE = 10;
    private final int QUEUESIZE = 1000;
    BlockingQueue<Request> e = new LinkedBlockingQueue<Request>(QUEUESIZE);
    
    public void startWorkers() {
        ExecutorService threadPool = Executors.newFixedThreadPool(POOLSIZE);
        for(int i=0; i<POOLSIZE; i++) {
            threadPool.execute(new Runnable() {
    
                @Override
                public void run() {
                    try {
                        final Request request = e.take();
                        doStuffWithRequest(request);
                    } catch (InterruptedException e) {
                        // LOG
                        // Shutdown worker thread.
                    }
                }
            });
        }
    }
    public void handleRequest(Request request) {
        if(!e.offer(request)) {
            //Cancel request, queue is full;
        }
    }
    
    私有静态接口请求{};
    私有最终整数池大小=10;
    私有最终整数队列大小=1000;
    BlockingQueue e=新的LinkedBlockingQueue(QUEUESIZE);
    公共空间startWorkers(){
    ExecutorService线程池=Executors.newFixedThreadPool(池大小);
    对于(int i=0;i我们最初是要处理这个问题的,但是如果您希望记录结果,那么s将通过一个且仅一个线程(其他线程阻塞并等待结果)封装初始化,并记录结果

    它还支持各种过期策略

    用法很简单,您可以使用初始化函数构造它:

    Map<Long, Foo> cache = new MapMaker().makeComputingMap(new Function<Long, Foo>() {
      public Foo apply(String key) {
        return … // init with expensive calculation
      }
    });
    

    第一个请求“key”的线程将是执行初始化的线程

    我知道问题是,有几个具有相同ID的请求可能会到达,并且只能处理一个。我知道“我不想多次执行相同的请求。”至于不要多次处理同一个请求,一个队列在这里是完美的匹配。我看不出对同时处理多少个请求有任何限制。顺便说一句:不同的机会不值得一次否决票。否决票被否决是为了补偿否决票。非常感谢您的输入!)我将评估您的解决方案,并在之后标记出最佳答案。非常感谢您的宝贵意见!祝贺您!
    Foo foo = cache.get("key");