我认为在运行多线程Java程序时我遇到了竞争条件。
这是一种置换算法,我想通过运行具有不同值的多个实例来加快速度。所以我用Main:
启动Main类中的线程Runnable[] mcl = new MCL[n1];
for (int thread_id = 0; thread_id < n1; thread_id ++)
{
mcl[thread_id] = new MCL(thread_id);
new Thread(mcl[thread_id]).start();
Thread.sleep(100);
}
它运行那些MCL类实例。
同样,我认为线程正在访问MCL类变量的相同内存空间,对吗?如果是这样,我该如何解决?
我正在尝试创建所有变量数组,其中一个维度与线程的Id相关,因此每个线程都在不同的索引上写入。这是一个很好的解决方案吗?:
int[] foo = new foo[thread_id];
答案 0 :(得分:3)
您不仅可以将线程安全视为事后的想法,还需要成为数据流设计中不可或缺的一部分。
开始,研究并学习以下主题:
1)同步块,互斥锁和最终变量。一个好的开始:Tutorial。我也很喜欢Josh Bloch的Effective Java,尽管几年前他还有编写正确Java程序的金块。
2)Oracle's Concurrency Tutorial
3)了解Executors。除非在最极端的情况下,否则您不必直接管理线程。见tutorial
如果在线程之间传递非线程安全对象,您将看到不可预测的结果。不可预测意味着分配可能永远不会出现在不同的线程之间,或者对象可能会处于无效状态(特别是如果您有多个成员字段,这些成员字段的数据彼此依赖)。
在没有看到MCL课程的情况下,我们无法向您提供有关危险内容的具体细节,但考虑到您发布的代码示例,我认为您应该退后一步并进行一些研究。从长远来看,它将节省您以正确的方式学习它的时间,而不是解决不正确的并发方案。
答案 1 :(得分:1)
如果要将线程数据保持分开,则将其作为实例变量存储在Runnables中(在启动其线程之前初始化每个Runnable)。不要在数组中保留对它的引用,这只会引起麻烦。
您可以使用CompletionService为Future中包含的每个任务获取计算值,因此在实际需要该值之前,不要等待它计算。评论员推荐的CompletionService和Executor之间的区别在于CompletionService使用Executor来执行任务,但它可以更容易地将数据恢复,see this answer。
以下是使用CompletionService的示例。我正在使用Callable而不是Runnable,因为我想得到一个结果:
public class CompletionServiceExample {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorCompletionService<BigInteger> service =
new ExecutorCompletionService<BigInteger>(executorService);
MyCallable task1 = new MyCallable(new BigInteger("3"));
MyCallable task2 = new MyCallable(new BigInteger("5"));
Future<BigInteger> future1 = service.submit(task1);
Future<BigInteger> future2 = service.submit(task2);
System.out.println("submitted tasks");
System.out.println("result1=" + future1.get() );
System.out.println("result2=" + future2.get());
executorService.shutdown();
}
}
class MyCallable implements Callable<BigInteger> {
private BigInteger b;
public MyCallable(BigInteger b) {
this.b = b;
}
public BigInteger call() throws Exception {
// do some number-crunching thing
Thread.sleep(b.multiply(new BigInteger("100")).longValue());
return b;
}
}
或者,您可以使用take方法在结果完成时检索结果:
public class TakeExample {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorCompletionService<BigInteger> service = new
ExecutorCompletionService<BigInteger>(executorService);
MyCallable task1 = new MyCallable(new BigInteger("10"));
MyCallable task2 = new MyCallable(new BigInteger("5"));
MyCallable task3 = new MyCallable(new BigInteger("8"));
service.submit(task1);
service.submit(task2);
service.submit(task3);
Future<BigInteger> futureFirst = service.take();
System.out.println(futureFirst.get());
Future<BigInteger> futureSecond = service.take();
System.out.println(futureSecond.get());
Future<BigInteger> futureThird = service.take();
System.out.println(futureThird.get());
executorService.shutdown();
}
}