Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使用CountDownLatch时如何正确同步/锁定_Java_Multithreading_Locking_Blocking - Fatal编程技术网

Java 使用CountDownLatch时如何正确同步/锁定

Java 使用CountDownLatch时如何正确同步/锁定,java,multithreading,locking,blocking,Java,Multithreading,Locking,Blocking,它归结为一个线程通过某种服务提交作业。作业在某些TPExecutor中执行。之后,该服务检查结果,并在某些情况下(作业超过最大重试次数等)在原始线程中引发异常。下面的代码片段大致说明了遗留代码中的此场景: import java.util.concurrent.CountDownLatch; public class IncorrectLockingExample { private static class Request { private final CountDownLat

它归结为一个线程通过某种服务提交作业。作业在某些TPExecutor中执行。之后,该服务检查结果,并在某些情况下(作业超过最大重试次数等)在原始线程中引发异常。下面的代码片段大致说明了遗留代码中的此场景:

import java.util.concurrent.CountDownLatch;

public class IncorrectLockingExample {

private static class Request {

    private final CountDownLatch latch = new CountDownLatch(1);

    private Throwable throwable;

    public void await() {
        try {
            latch.await();
        } catch (InterruptedException ignoredForDemoPurposes) {
        }
    }

    public void countDown() {
        latch.countDown();
    }

    public Throwable getThrowable() {
        return throwable;
    }

    public void setThrowable(Throwable throwable) {
        this.throwable = throwable;
    }

}

private static final Request wrapper = new Request();

public static void main(String[] args) throws InterruptedException {

    final Thread blockedThread = new Thread() {
        public void run() {
            wrapper.await();
            synchronized (wrapper) {
                if (wrapper.getThrowable() != null)
                    throw new RuntimeException(wrapper.getThrowable());
            }
        }
    };

    final Thread workingThread = new Thread() {
        public void run() {
            wrapper.setThrowable(new RuntimeException());
            wrapper.countDown();

        }
    };

    blockedThread.start();
    workingThread.start();

    blockedThread.join();
    workingThread.join();
}
}

有时,(在我的机器上不可复制,但发生在16核服务器机器上)异常不会报告给原始线程。我认为这是因为before不是强制的(例如,“倒计时”发生在“setThrowable”之前),程序继续工作(但应该失败)。 如果您能为我解决这个案子提供帮助,我将不胜感激。 限制条件是:在一周内发布,对现有代码库的影响最小。

我想您需要

private volatile Throwable throwable
您是否尝试过使用内置的ExecutorService,并为您这样做。下面的照片

future1 := result
future2  threw java.lang.IllegalStateException
future3  timed out
代码是

public static void main(String... args)  {
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<String> future1 = executor.submit(new Callable<String>() {
        public String call() throws Exception {
            return "result";
        }
    });

    Future<String> future2 = executor.submit(new Callable<String>() {
        public String call() throws Exception {
            throw new IllegalStateException();
        }
    });

    Future<String> future3 = executor.submit(new Callable<String>() {
        public String call() throws Exception {
            Thread.sleep(2000);
            throw new AssertionError();
        }
    });

    printResult("future1", future1);
    printResult("future2", future2);
    printResult("future3", future3);
    executor.shutdown();
}

private static void printResult(String description, Future<String> future) {
    try {
        System.out.println(description+" := "+future.get(1, TimeUnit.SECONDS));
    } catch (InterruptedException e) {
        System.out.println(description+"  interrupted");
    } catch (ExecutionException e) {
        System.out.println(description+"  threw "+e.getCause());
    } catch (TimeoutException e) {
        System.out.println(description+"  timed out");
    }
}
如果您不打算在JDK中重复使用代码,那么仍然值得一读,这样您就可以了解他们使用的任何技巧。

上述代码(现在已更新)在不使用进一步的同步机制的情况下应能按预期工作。通过使用
CountDownLatch
await()
countdown()
方法强制执行内存屏障及其相应的“发生在”关系

从:

“释放”同步器方法(如Lock.unlock、Semaphore.release和CountDownLatch.countDown)之前的操作发生在另一个线程中的同一同步器对象上成功“获取”方法(如Lock.Lock、Semaphore.acquire、Condition.await和CountDownLatch.await)之后的操作之前


如果您定期处理并发问题,请为自己准备一份,这是Java并发圣经,它在您的书架上很值得一看:-)。

250 KLOC项目在这里完全多线程,在16核上工作等。我们经常使用“高级”多线程功能,如倒计时闩锁。我们使用诸如Object的wait()方法和Thread的join()方法之类的低级方法的次数?零。在我看来,现在在默认API中有足够的高级并发功能,您不需要根据Java特性重新发明任何损坏的轮子+1对Peter Lawrey的回答。@Webinator:OP正在使用“高级”
CountDownLatch
功能用于此处的设计目的之一。您确定上面的代码没有按预期运行吗?在您进行更正后,我认为没有理由不这样做。我不知道wait()是
ExecutorService
建议的一个坏轮子/+1,但是
volatile
关键字在这里没有任何用处。感谢您提供有关FutureTask的示例。看起来这就是我们将来必须升级到的(而不是使用CountDownLatch和throwable的组合)。不幸的是,在保证之前,我的情况略有不同。如果FutureTask需要“volatile”运行程序,因为callable(innerRunAndReset方法)不能保证之前发生。CountDownLatch等待/倒计时保证在之前发生(感谢您指向API文档,我不知道这一事实)。但是,“先发制人”本身并不能保证在CountDownLatch.countDown之前所做更改的可见性。因此,唯一的解决方案是将throwable设置为volatile,或者在“synchronized(throwable){…}”块中进行更新。这是否意味着sence@PetroSemeniuk:不,before关系保证了跨越内存屏障的所有以前写入的可见性,即,在
workingRead
before
countDown()
中执行的任何操作都将在相应的
wait()
返回后在
blockedThread
中可见。仅为了创建内存屏障而使用
synchronized
被认为是不好的(这就是
volatile
的用途),无论如何,这里不需要
volatile
,因为使用
CountDownLatch
方法的线程同步已经创建了所需的内存屏障,并且发生在关系之前。
/**
 * The thread running task. When nulled after set/cancel, this
 * indicates that the results are accessible.  Must be
 * volatile, to ensure visibility upon completion.
 */