如何在Java中的线程之间隔离变量?

时间:2014-03-21 13:28:06

标签: java multithreading concurrency race-condition

我认为在运行多线程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];

2 个答案:

答案 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();
    }
}