锁存不同任务类型

时间:2018-06-27 02:31:57

标签: java concurrency

我正在寻找以下问题的Java并发解决方案。

正在运行一些任务,还有一段代码C。

  • C必须等待所有任务完成。 (超时)

  • 在C完成之前不能开始任何任务。

我浏览了java.concurrency包,发现了一些有趣的东西,但是似乎没有什么工作很正确:

  • 定相器将允许一种方式阻止,但不允许两种方式。
  • 信号量,ForkJoinTasks和其他具有计数器类型的功能,但似乎都没有我想要的功能。

我相信我可以使用移相器和锁来构造一些东西:

void C() {
    synchronized(lock) {
        phaser.awaitAdvanceInterruptibly(phase, 1, TimeUnit.SECONDS);
        // Start work anyway if a task is taking too long.
        doWork();
    }
}

void someTask() {
    synchronized(lock) {
        phaser.register();
    }
    doTask().thenRun(
        () -> phaser.arriveAndDeregister()
    );
}

现在,尽管我相当确定这是可行的,但我也意识到尝试构建自己的并发解决方案是一个坏主意。有更好的方法吗?

如果没有,我将如何使用phase参数?

编辑:此问题在涉及Web客户端连接的项目中,因此任务无法预期地到达。但是,通过更仔细的设计可以避免这种情况。

2 个答案:

答案 0 :(得分:0)

这是一个特殊的用例,我认为我们需要使用多个并发实用程序进行协调。下面的程序应该做到这一点。请随时发布任何不清楚的问题-

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class TestClass {

    private volatile int numOfActiveTasks = 0;
    private Semaphore cSemaphore = new Semaphore(1);
    private Semaphore taskSemaphore = new Semaphore(1);
    private Object tasksLock = new Object();

    //Test method
    public static void main(String[] args) throws IOException {
        TestClass testClass = new TestClass();

        //Launch some task threads
        ExecutorService taskES = Executors.newFixedThreadPool(2);
        IntStream.range(1, 11).forEach((i) -> taskES.submit(() -> {
            try {
                testClass.executeTask();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));

        //Launch some C threads
        ExecutorService cES = Executors.newFixedThreadPool(2);
        IntStream.range(1, 5).forEach((i) -> cES.submit(() -> {
            try {
                testClass.C();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));

        taskES.shutdown();
        cES.shutdown();
    }


    void C() throws InterruptedException {
        try {
            cSemaphore.acquire();

            //If tasks are running, wait at-least n seconds
            this.taskSemaphore.tryAcquire(1, TimeUnit.SECONDS);

            print("C started running");
            doCsWork();

        } finally {
            cSemaphore.release();
            print("C stopped running");
        }
    }

    void executeTask() throws InterruptedException {

        //Do not start while C is running
        cSemaphore.acquire();
        cSemaphore.release();

        synchronized (tasksLock) {
            ++numOfActiveTasks;
            taskSemaphore.tryAcquire();
            print("A task started running. Total " + numOfActiveTasks + " tasks running");
        }

        doTasksWork();

        synchronized (tasksLock) {
            --numOfActiveTasks;
            if (numOfActiveTasks == 0) {
                taskSemaphore.release();
            }
            print("A task stopped running. Total " + numOfActiveTasks + " tasks remaining");
        }
    }

    void doCsWork() throws InterruptedException {
        Thread.sleep(1000);
    }

    void doTasksWork() throws InterruptedException {
        Thread.sleep(2000);
    }

    void print(String message) {
        System.out.println(message);
    }

}

答案 1 :(得分:0)

我在java.util.concurrent.locks中找到了解决此问题的解决方案,这对我的用例来说是完美的。

StampedLock lock;

void C() {
    long stamp = lock.tryWriteLock(1, TimeUnit.SECONDS);
    doWork();
    lock.unlockWrite(stamp);
}

void someTask() {
    long stamp = lock.readLock();
    doTask().thenRun(() -> lock.unlockRead(stamp));
}

具有StampedLock类的键是readLock()不是互斥的,而writeLock()是互斥的。它也支持超时,类似于常规的Lock。