【发布时间】:2020-03-08 21:32:03
【问题描述】:
我正在努力使用一些应该使用线程计算平方和的练习代码。 出于某种原因,尽管使用了锁,但我得到的结果不一致,还要确保我锁定的是对象而不是本地项目/变量等。
当我添加日志记录以了解行为时,代码运行速度要慢得多并且执行得很好。减少到一个线程时也是如此。但是 - 一旦我运行多个线程,它的行为就无法预测。 结果应该是
我的代码:
package com.yaniv.concurrency;
import java.sql.Timestamp;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class SumSquaresSync implements Runnable {
ReentrantLock nextLock = new ReentrantLock();
ReentrantLock sumLock = new ReentrantLock();
Long sum = new Long(0);
int min, max;
Integer next = new Integer(0);
int threadBatchSize = 10;
static final boolean LOG = false;
@Override
public void run() {
long localSum = 0;
int[] batch = new int[threadBatchSize];
while (next <= max) {
nextLock.lock();
if (this.next <= max) {
{
for (int i = 0; i < threadBatchSize; i++) {
batch[i] = ((next + i <= max) ? (next + i) : 0);
}
next += threadBatchSize;
}
}
nextLock.unlock();
if (LOG) {
synchronized (System.out) {
System.out.print(Thread.currentThread().getName() + " got batch " + batch.toString() + ": ");
for (int i = 0; i < threadBatchSize; i++) {
System.out.print(batch[i] + ", ");
}
System.out.println();
}
}
for (int i : batch) {
localSum += Math.pow(i, 2);
}
}
sumLock.lock();
sum += localSum;
sumLock.unlock();
if (LOG) {
safePrintln(Thread.currentThread().getName() + " terminated, localSum = " + localSum);
}
}
private long executeSumSquares(int min, int max, int numberOfThreads, int threadBatchSize) throws Exception {
this.min = min;
this.max = max;
this.next = min;
this.threadBatchSize = threadBatchSize;
this.sum = 0L;
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
for (int i = 0; i < numberOfThreads; i++) {
if (LOG) {
System.out.format("Adding thread %d%n", i);
}
executorService.execute(new SumSquaresSyncThread(this));
}
executorService.shutdown();
executorService.awaitTermination(5_000L, TimeUnit.MILLISECONDS);
return sum;
}
public static void main(String[] args) throws Exception {
SumSquaresSync SumSquaresSync = new SumSquaresSync();
long total;
int iteration = 0;
Timestamp startTime, endTime;
do {
iteration++;
startTime = new Timestamp(System.currentTimeMillis());
total = SumSquaresSync.executeSumSquares(1, 10000, 1, 5);
endTime = new Timestamp(System.currentTimeMillis());
System.out.println("==========================================");
System.out.format("Total sum: %,8d, elapsed time %d, iteration %d%n", total, (endTime.getTime() - startTime.getTime()), iteration);
System.out.println("==========================================");
} while (iteration < 10); //(total == 333383335000L);
}
public void safePrintln(String s) {
synchronized (System.out) {
System.out.println(s);
}
}
public void safePrint(String s) {
synchronized (System.out) {
System.out.print(s);
}
}
}
结果:
Total sum: 347,938,671,335, elapsed time 16, iteration 1
Total sum: 342,283,818,850, elapsed time 10, iteration 2
Total sum: 336,257,779,565, elapsed time 10, iteration 3
Total sum: 336,233,345,285, elapsed time 9, iteration 4
Total sum: 337,663,242,000, elapsed time 8, iteration 5
Total sum: 336,779,784,290, elapsed time 10, iteration 6
Total sum: 335,474,886,225, elapsed time 10, iteration 7
Total sum: 338,825,524,135, elapsed time 8, iteration 8
Total sum: 335,820,751,880, elapsed time 10, iteration 9
Total sum: 335,083,150,300, elapsed time 8, iteration 10
正确的结果应该是 333,383,335,000。 谁能告诉我错过了什么?
【问题讨论】:
-
您的
while (next <= max)支票不受锁保护。您可能出于这个原因在锁内再次检查它,但for (int i : batch) { localSum += Math.pow(i, 2); }部分不在内部条件内,因此localSum有时可能会添加两次。 -
问题已解决,感谢@CherryDT!我已经为此苦苦挣扎了好几个小时。真的很感激!
-
我意识到它作为一个完整的答案实际上是有意义的。添加它,因此您可以接受它作为解决方案。不客气!
-
Re,“当我添加日志时......代码......执行得很好。”到过那里。做到了。有时您可以通过将事件记录到内存中的缓冲区而不是写入文件来获得线索,然后再转储缓冲区。 YMMV。
-
这是一个有用的提示 Solomon。谢谢!
标签: java multithreading reentrantlock