JAguirre
JAguirre

Reputation: 45

How to isolate variables between threads in Java?

I think I'm having race conditions when running my multithreaded Java program.

It's a permutation algorithm, which I want to speed up by running multiple instances with different values. So I start the threads in Main class with:

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);
    }

And it runs those MCL classes instances.

Again, I think threads are accessing the same memory space of the MCL class variables, am I right? If so, how can I solve this?

I'm trying to make all variables arrays, where one of the dimensions is related to an Id of the thread, so that each thread writes on a different index. Is this a good solution?:

int[] foo = new foo[thread_id];

Upvotes: 1

Views: 1406

Answers (2)

Nathan Hughes
Nathan Hughes

Reputation: 96434

If you want to keep the thread data separate store it as instance variables in the Runnables (initializing each Runnable before starting its thread). Don't keep a reference to it in an array, that's just inviting trouble.

You can use a CompletionService to get a computed value back for each task wrapped in a Future, so you don't wait for it to be calculated until you actually need the value. The difference between a CompletionService and an Executor, which the commentors are recommending, is that the CompletionService uses an Executor for executing tasks, but it makes it easier to get your data back out, see this answer.

Here's an example of using a CompletionService. I'm using Callable instead of Runnable because I want to get a result back:

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;
    }
}

Alternatively you can use the take method to retrieve results as they get completed:

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

Upvotes: 1

Jason Nichols
Jason Nichols

Reputation: 11753

You can't just bolt on thread safety as an afterthought, it needs to be an integral part of your data flow design.

To start, research and learn the following topics:

1) Synchronized blocks, mutexes, and final variables. A good place to start: Tutorial. I also love Josh Bloch's Effective Java, which although a few years old has golden nuggets for writing correct Java programs.

2) Oracle's Concurrency Tutorial

3) Learn about Executors. You shouldn't have to manage threads directly except in the most extreme cases. See this tutorial

If you pass non thread safe objects between threads you're going to see unpredictable results. Unpredictable means assignments may never show up between different threads, or objects may be left in invalid states (especially if you've got multiple member fields that have data dependent on each other).

Without seeing the MCL class we can't give you any specific details on what's dangerous, but given the code sample you've posted I think you should take a step back and do some research. In the long run it will save you time to learn it the right way rather than troubleshoot an incorrect concurrency scheme.

Upvotes: 3

Related Questions