一种高效的并发数据结构,用于等待计算值(或超时)

时间:2018-04-12 11:02:39

标签: java multithreading concurrency java.util.concurrent

我希望一些并发专家可以提供建议,因为我不打算重写可能存在的东西。

想象问题;我有一个网络连接来调用寻找他们唯一的计算结果(他们提供的密钥,以便检索他们的结果) - 但结果可能没有计算YET所以我想连接等待(块)对于UP TO n 秒,然后放弃并告诉他们我(还)没有他们的结果(计算值的计算时间是非确定性的)。类似的东西;

String getValue (String key)
    {
        String value = [MISSING_PIECE_OF_PUZZLE].getValueOrTimeout(key, 10, TimeUnit.SECONDS)
        if (value == null)
            return "Not computed within 10 Seconds";
        else
            return "Value was computed and was " + value;

    }

然后有另一个正在进行计算的线程(计算线程) - 类似于;

public void writeValues()
{
....
[MISSING_PIECE_OF_PUZZLE].put(key, computedValue)
}

在这种情况下,有很多线程在后台中工作,以计算最终由网络连接获取的值。 Web连接对计算的内容和计算执行时没有任何控制或权限 - 正如我所说的 - 这是在后台的池中完成的,但这些线程可以在计算完成时发布(它们是如何执行的)这个问题的要点)。发布消息可能消费与否 - 取决于是否有任何订阅者对此计算值感兴趣。

由于这些是阻塞的Web连接 - 我可能有1000个并发连接等待(订阅)其特定的计算值,因此这样的解决方案需要非常轻松地阻止资源。我最接近的是这个SO question我会进一步探索但是想在我自己写这篇文章之前检查一下我是不是错过了一些明显的东西?

enter image description here

2 个答案:

答案 0 :(得分:3)

我认为你应该使用Future它能够在一个单独的线程中计算数据,并在等待答案时阻止所请求的时间段。注意如果超过3秒后它会抛出异常

public class MyClass {

// Simulates havy work that takes 10 seconds
private static int getValueOrTimeout() throws InterruptedException {
    TimeUnit.SECONDS.sleep(10);
    return 123;
}


public static void main(String... args) throws InterruptedException, ExecutionException {
    Callable<Integer> task = () -> {
        Integer val = null;
        try {
            val = getValueOrTimeout();
        } catch (InterruptedException e) {
            throw new IllegalStateException("task interrupted", e);
        }

        return val;
    };

    ExecutorService executor = Executors.newFixedThreadPool(1);
    Future<Integer> future = executor.submit(task);

    System.out.println("future done? " + future.isDone());

    try {
        Integer result = future.get(3, TimeUnit.SECONDS);
        System.out.print("Value was computed and was : " + result);
    } catch (TimeoutException ex) {
        System.out.println("Not computed within 10 Seconds");
    }
}

}

答案 1 :(得分:1)

在查看问题中的更改之后,我想建议使用BlockingQueue的不同方法,在这种情况下,生产者逻辑与消费者完全分离,因此您可以执行类似这样的操作

public class MyClass {

private static BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);

private static Map<String, String> dataComputed = new ConcurrentHashMap<>();

public static void writeValues(String key) {
    Random r = new Random();
    try {
        // Simulate working for long time
        TimeUnit.SECONDS.sleep(r.nextInt(11));
        String value = "Hello there fdfsd" + Math.random();
        queue.offer(value);
        dataComputed.putIfAbsent(key, value);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

}

private static String getValueOrTimeout(String key) throws InterruptedException {
    String result = dataComputed.get(key);
    if (result == null) {
        result = queue.poll(10, TimeUnit.SECONDS);
    }
    return result;
}


public static void main(String... args) throws InterruptedException, ExecutionException {
    String key = "TheKey";

    Thread producer = new Thread(() -> {
        writeValues(key);
    });

    Thread consumer = new Thread(() -> {
        try {
            String message = getValueOrTimeout(key);
            if (message == null) {
                System.out.println("No message in 10 seconds");
            } else {
                System.out.println("The message:" + message);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });

    consumer.start();
    producer.start();

}

}

有了这个说我必须同意@earned让客户端线程等待不是一个好方法而是我建议使用一个WebSocket,它让你能够在准备就绪时将数据推送到客户端你可以找到这里有很多关于WebSocket的教程,例如ws tutorial