Java 线程运行方法内部使用的方法的同步
我想对几组输入值执行并行计算。我是否需要同步Java 线程运行方法内部使用的方法的同步,java,multithreading,concurrency,java-8,Java,Multithreading,Concurrency,Java 8,我想对几组输入值执行并行计算。我是否需要同步计算(a,b,inputIndex)方法 private static final String FORMULA = "(#{a} + #{b}) * (#{a} + #{b} * #{b} - #{a})"; private List<Pair<Integer, Integer>> input = Arrays.asList( new ImmutablePair<>(1, 2), n
计算(a,b,inputIndex)
方法
private static final String FORMULA = "(#{a} + #{b}) * (#{a} + #{b} * #{b} - #{a})";
private List<Pair<Integer, Integer>> input = Arrays.asList(
new ImmutablePair<>(1, 2),
new ImmutablePair<>(2, 2),
new ImmutablePair<>(3, 1),
new ImmutablePair<>(4, 2),
new ImmutablePair<>(1, 5)
);
private List<String> output = new ArrayList<>(Arrays.asList("", "", "", "", ""));
public void calculate() {
IntStream.range(0, input.size()).forEach(idx -> {
Pair<Integer, Integer> pair = input.get(idx);
Thread threadWrapper = new Thread(
() -> this.calculate(pair.getLeft(), pair.getRight(), idx)
);
threadWrapper.start();
});
try {
Thread.sleep(4000); // waiting for threads to finish execution just in case
System.out.println("Calculation result => " + output);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void calculate(Integer a, Integer b, int inputIndex) {
System.out.println("Thread with index " + inputIndex + " started calculation.");
Evaluator eval = new Evaluator();
eval.putVariable("a", a.toString());
eval.putVariable("b", b.toString());
try {
String result = eval.evaluate(FORMULA);
Thread.sleep(3000);
output.set(inputIndex, result);
System.out.println("Thread with index " + inputIndex + " done.");
} catch (EvaluationException | InterruptedException e) {
e.printStackTrace();
}
}
private static final String FORMULA=“(#{a}+#{b})*(#{a}+#{b}*#{b}-#{a})”;
私有列表输入=Arrays.asList(
新的不可变对(1,2),
新的不可变对(2,2),
新的不可变对(3,1),
新的不可变对(4,2),
新的不可变对(1,5)
);
私有列表输出=新的ArrayList(Arrays.asList(“,”,“,”,“,”,”);
公共空间计算(){
IntStream.range(0,input.size()).forEach(idx->{
Pair Pair=input.get(idx);
ThreadThreadWrapper=新线程(
()->this.calculate(pair.getLeft(),pair.getRight(),idx)
);
threadWrapper.start();
});
试一试{
sleep(4000);//等待线程完成执行,以防万一
System.out.println(“计算结果=>”+输出);
}捕捉(中断异常e){
e、 printStackTrace();
}
}
私有void计算(整数a、整数b、整数inputIndex){
System.out.println(“带索引的线程”+inputIndex+“已开始计算”);
Evaluator eval=新的Evaluator();
eval.putVariable(“a”,a.toString());
eval.putVariable(“b”,b.toString());
试一试{
字符串结果=评估(公式);
睡眠(3000);
输出设置(输入索引、结果);
System.out.println(“带索引的线程”+inputIndex+“完成”);
}捕获(EvaluationException | InterruptedException e){
e、 printStackTrace();
}
}
因为如果
calculate
方法的代码在run
方法的Runnable
中,我就不需要这样做了。(另外,我认为我不需要同步收集,因为对于输入
我只通过索引获取数据,对于输出
我将元素放在特定位置)重要的是要强调,尝试代码并获得正确的输出不足以证明程序的正确性,尤其是涉及多线程时。在您的情况下,它可能是偶然工作的,原因有两个:
- 您的代码中有调试输出语句,即,
,它引入了线程同步,就像在参考实现中,System.out.println(…)
内部同步一样PrintStream
- 您的代码很简单,运行时间不够长,无法通过JVM进行深入优化
为了获得正确的程序,即使将
calculate(整数a、整数b、int-inputIndex)
更改为synchronized
方法也是不够的。同步仅适用于在同一对象上同步线程之前建立关系
您的启动方法calculate()
在这个实例上不同步,并且它也不执行任何其他足以与计算线程建立“先发生后发生”关系的操作(如调用Thread.join()
。它只调用Thread.sleep(4000)
,这显然不能保证其他线程在这段时间内完成。此外:
需要注意的是,Thread.sleep
和Thread.yield
都没有任何同步语义。特别是,在调用Thread.sleep
或Thread.yield
之前,编译器不必将寄存器中缓存的写入刷新到共享内存中,也不必重新加载值c调用Thread.sleep
或Thread.yield
后,在寄存器中进行ached
例如,在以下(断开的)代码片段中,假设this.done
是一个非易失性的
字段:
编译器可以自由读取字段this.done
一次,并在每次执行循环时重用缓存的值。这意味着循环永远不会终止,即使另一个线程更改了this.done
的值
请注意,示例中所述的this.done
也适用于列表的支持数组的数组元素。如果您没有使用不可变的字符串
实例,效果可能会更糟
但是没有必要使整个方法同步
,只有数据交换必须是线程安全的。最干净的解决方案是使整个方法没有副作用,即将签名改为字符串计算(整数a、整数b)
并让该方法返回结果,而不是操作共享数据结构。如果该方法没有副作用,则不需要任何同步
调用方必须将结果值组合到列表中
,但由于您已经在使用流API,此操作是免费的:
private static final String FORMULA = "(#{a} + #{b}) * (#{a} + #{b} * #{b} - #{a})";
private List<Pair<Integer, Integer>> input = Arrays.asList(
new ImmutablePair<>(1, 2),
new ImmutablePair<>(2, 2),
new ImmutablePair<>(3, 1),
new ImmutablePair<>(4, 2),
new ImmutablePair<>(1, 5)
);
public void calculate() {
List<String> output = input.parallelStream()
.map(pair -> this.calculate(pair.getLeft(), pair.getRight()))
.collect(Collectors.toList());
System.out.println("Calculation result => " + output);
}
private String calculate(Integer a, Integer b) {
System.out.println(Thread.currentThread()+" does calculation of ("+a+","+b+")");
Evaluator eval = new Evaluator();
eval.putVariable("a", a.toString());
eval.putVariable("b", b.toString());
try {
String result = eval.evaluate(FORMULA);
Thread.sleep(3000);
System.out.println(Thread.currentThread()+" with ("+a+","+b+") done.");
return result;
} catch (EvaluationException | InterruptedException e) {
throw new RuntimeException(e);
}
}
private static final String FORMULA=“(#{a}+#{b})*(#{a}+#{b}*#{b}-#{a})”;
私有列表输入=Arrays.asList(
新的不可变对(1,2),
新的不可变对(2,2),
新的不可变对(3,1),
新的不可变对(4,2),
新的不可变对(1,5)
);
公共空间计算(){
列表输出=输入。并行流()
.map(pair->this.calculate(pair.getLeft(),pair.getRight())
.collect(Collectors.toList());
System.out.println(“计算结果=>”+输出);
}
私有字符串计算(整数a、整数b){
System.out.println(Thread.currentThread()+“进行(“+a+”,“+b+”)的计算);
Evaluator eval=新的Evaluator();
eval.putVariable(“a”,a.toString());
eval.putVariable(“b”,b.toString());
private static final String FORMULA = "(#{a} + #{b}) * (#{a} + #{b} * #{b} - #{a})";
private List<Pair<Integer, Integer>> input = Arrays.asList(
new ImmutablePair<>(1, 2),
new ImmutablePair<>(2, 2),
new ImmutablePair<>(3, 1),
new ImmutablePair<>(4, 2),
new ImmutablePair<>(1, 5)
);
public void calculate() {
List<String> output = input.parallelStream()
.map(pair -> this.calculate(pair.getLeft(), pair.getRight()))
.collect(Collectors.toList());
System.out.println("Calculation result => " + output);
}
private String calculate(Integer a, Integer b) {
System.out.println(Thread.currentThread()+" does calculation of ("+a+","+b+")");
Evaluator eval = new Evaluator();
eval.putVariable("a", a.toString());
eval.putVariable("b", b.toString());
try {
String result = eval.evaluate(FORMULA);
Thread.sleep(3000);
System.out.println(Thread.currentThread()+" with ("+a+","+b+") done.");
return result;
} catch (EvaluationException | InterruptedException e) {
throw new RuntimeException(e);
}
}