我有一个计划的方法调用,该方法在计划的时间调用以下方法:
private void doSomething(Map<String, String> someArguments) throws CustomException {
MyEnum runType = getRunType(someArguments);
switch (runType) {
case FRUIT:
new FruitClass().workNow();
break;
case VEGETABLE:
new VegetableClass().workNow();
break;
default:
// log that the type is not known
}
}
workNow
的方法签名如下:
workNow() throws CustomException
workNow
方法运行几分钟并完成一些工作。我的问题是,当workNow
(或FRUIT
)中的一个VEGETABLE
正在进行,而同一类型(例如,FRUIT
)发生另一次调用时,它会创建一个新的{ {1}}实例,并开始并行执行其FruitClass
。
如何控制此行为?我希望通过第二个对象进行的第二次调用要等到通过第一个对象的第一个workNow
完成之前。
要澄清:
可以并行调用workNow
和FRUIT
。我想控制相同类型的并行调用。两个或更多VEGETABLE
或两个或更多FRUIT
。
我无法将VEGETABLE
和FruitClass
设为单例。我需要在VegetableClass
周围添加一些包装代码才能按需工作。
答案 0 :(得分:3)
对一个类对象进行同步,这将足以避免在完成之前创建另一个类:
private void doSomething(Map<String, String> someArguments) {
MyEnum runType = getRunType(someArguments);
switch (runType) {
case FRUIT:
synchronized (FruitClass.class){
new FruitClass().workNow();
}
break;
case VEGETABLE:
synchronized (VegetableClass.class){
new VegetableClass().workNow();
}
break;
default:
// log that the type is not known
}
}
类对象上的 synchronized
使用类实例作为监视器。
类对象实际上是一个单例(该对象表示运行时的类元数据),并且该块中只能有一个线程。
答案 1 :(得分:1)
我想到的是一对解决方案:
static final String FRUIT = "FRUIT";
static final String VEGETABLE = "VEGETABLE";
private void doSomething(Map<String, String> someArguments) {
MyEnum runType = getRunType(someArguments);
switch (runType) {
case FRUIT:
synchronized (FRUIT){
new FruitClass().workNow();
}
break;
case VEGETABLE:
synchronized (VEGETABLE){
new VegetableClass().workNow();
}
break;
default:
// log that the type is not known
}
}
这可能比使用class
对象更好,因为它们会更重并且会占用内存。
如果有多种情况,并且不需要类级别String
,这是对Solution-1的增强。
private void doSomething(Map<String, String> someArguments) {
MyEnum runType = getRunType(someArguments);
synchronized(runType.toString().intern()) {//This prevents 2 FRUITs or 2 VEGETABLEs from entering
switch (runType) {
case FRUIT:
new FruitClass().workNow();
break;
case VEGETABLE:
new VegetableClass().workNow();
break;
default:
// log that the type is not known
}
}
}
这两个示例均在稍有不同的示例中进行了测试,但是很重要。
答案 2 :(得分:0)
肯定有很多方法可以解决这个问题。我认为最简单的方法是对每种类型的任务使用单线程池:
//one pool per runType
private final ExecutorService fruitService = Executors.newSingleThreadExecutor();
private final ExecutorService vegService = Executors.newSingleThreadExecutor();
然后:
private void doSomething(Map<String, String> someArguments) {
MyEnum runType = getRunType(someArguments);
CompletableFuture<Void> result;
switch (runType) {
case FRUIT:
result = CompletableFuture.runAsync(() ->
new FruitClass().workNow(), fruitService)
.exceptionally((exception) -> {
if (exception instanceof CustomException) {
System.out.println("Failed with custom exception...");
}
return null; // returning Void
});
break;
case VEGETABLE:
result = CompletableFuture.runAsync(() ->
new VegetableClass().workNow(), vegService)
.exceptionally((exception) -> {
if (exception instanceof CustomException) {
System.out.println("Failed with custom exception...");
}
return null; // returning Void
});
break;
default:
throw new RuntimeException();
}
result.join();
}
这只是强制并发调用等待资源,并且相同类型的2个任务不会同时运行。
它还提供了异步执行的其他好处,尽管您可以根据需要显式阻塞以等待结果。