Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way of running two threads alternatively?

Update: see bottom of this question for a complete answer.

I want to run a secondary thread, so that my main thread and my secondary thread perform operations alternatively (no, I don't want to do all the operations in the main thread, it is for a unit test).

I came up to two different solutions, I don't know which is the best, and I have questions regarding the first one:

Using Exchanger

I came up to something using an Exchanger (while I don't want to exchange only one object).

@Test
public void launchMyTest() {
    /**
     * An anonymous class to set some variables from a different thread
     */
    class ThreadTest extends Thread {
        //declare some various attributes that will be set
        //NOT DECLARED VOLATILE
        ...

        public final Exchanger<Integer> exchanger = new Exchanger<Integer>();

        @Override
        public void run() {
            try {
                //start of the synchronization 
                int turn = 1;
                while (turn != 2) {
                    turn = this.exchanger.exchange(turn);
                }

                //do some work and set my various variables
                ...

                //main thread's turn
                turn = 1;
                this.exchanger.exchange(turn);
                //wait for this thread's turn
                while (turn != 2) {
                    turn = this.exchanger.exchange(turn);
                }

                //redo some other work and reset the various variables
                ...

                //main thread's turn
                turn = 1;
                this.exchanger.exchange(turn);

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } 
        }
    }


    try {
        //some work in the main thread
        ....

        //launch the job in the second thread
        ThreadTest test = new ThreadTest();
        test.start();
        //start of the synchronization
        int turn = 2;
        test.exchanger.exchange(turn);
        //wait for this thread's turn
        while (turn != 1) {
            turn = test.exchanger.exchange(turn);
        }

        //run some tests using the various variables of the anonymous class
        ....

        //now, relaunch following operations in the second thread
        turn = 2;
        test.exchanger.exchange(turn);
        //wait for this thread's turn
        while (turn != 1) {
            turn = test.exchanger.exchange(turn);
        }

        //do some other tests using the various variables of the anonymous class
        //...

    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

Question:

  • Am I correct that the exchange method performs memory synchronization, just as much as using a Lock?

Using Condition

Another solution using a Condition:

@Test
public void launchMyTest() {
    /**
     * An anonymous class to set some variables from a different thread
     */
    class ThreadTest extends Thread {
        //declare some various attributes that will be set
        //NOT DECLARED VOLATILE
        ...

        public final Lock lock = new ReentrantLock();
        public final Condition oneAtATime = lock.newCondition();
        public int turn = 1;

        @Override
        public void run() {
            this.lock.lock();
            try {
                //do some work and set my various variables
                ...

                //main thread's turn
                this.turn = 1;
                this.oneAtATime.signal();

                //wait for this thread's turn
                while (this.turn != 2) {
                    this.oneAtATime.await();
                }

                //redo some other work and reset the various variables
                ...

                //main thread's turn
                this.turn = 1;
                this.oneAtATime.signal();

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                this.lock.unlock();
            }
        }
    }


    ThreadTest test = new ThreadTest();
    test.lock.lock();
    try {
        //some work in the main thread
        ....

        //launch the job in the second thread
        test.turn = 2;
        test.start();
        //wait for this thread's turn
        while (test.turn != 1) {
            test.oneAtATime.await();
        }

        //run some tests using the various variables of the anonymous class
        ....

        //now, relaunch following operations in the second thread
        test.turn = 2;
        test.oneAtATime.signal();
        //wait for this thread's turn
        while (test.turn != 1) {
            test.oneAtATime.await();
        }

        //do some other tests using the various variables of the anonymous class
        //...

    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        test.lock.unlock();
    }
}

It seems to me a bit more complicated.

Conclusion

What do you think is the best solution? Am I doing it right, or do I miss another obvious solution?

I didn't use a CountDownLatch as I want to run several operations alternatively, and the CountDownLatch cannot be reset. And I didn't find that the CyclicBarrier was making the code simpler... (actually I didn't totally understand how to use it, but it didn't look simpler than using Exchanger or Condition)

Thank you.

Update

@Clément MATHIEU provided different examples of how to achieve this, in comments of its accepted answer, see: https://gist.github.com/cykl/5131021

There are three examples, one using a CyclicBarrier, another one using an Exchanger, and a last one using 2 Semaphores. While he is right to say that the "more expressive is the semaphore based one", I chose to use an Exchanger for simplicity. My unit test became:

@Test
public void launchMyTest() {
    /**
     * An anonymous class to set some variables from a different thread
     */
    class ThreadTest extends Thread {
        //declare some various attributes that will be set
        //NOT DECLARED VOLATILE
        ...
        public final Exchanger<Integer> exchanger = new Exchanger<Integer>();

        @Override
        public void run() {
            try {
                //do some work and set my various variables
                ...

                //main thread's turn
                this.exchanger.exchange(null);
                //wait for this thread's turn
                this.exchanger.exchange(null);

                //redo some other work and reset the various variables
                ...

                //main thread's turn
                this.exchanger.exchange(null);

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } 
        }
    }


    try {
        //some work in the main thread
        ....

        //launch the job in the second thread
        ThreadTest test = new ThreadTest();
        test.start();
        //wait for this thread's turn
        test.exchanger.exchange(null);

        //run some tests using the various variables of the anonymous class
        ....

        //now, relaunch following operations in the second thread
        test.exchanger.exchange(null);
        //wait for this thread's turn
        test.exchanger.exchange(null);

        //do some other tests using the various variables of the anonymous class
        //...

    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}
like image 234
FBB Avatar asked Mar 10 '13 16:03

FBB


Video Answer


2 Answers

exchanger looks right. After watching http://www.youtube.com/watch?v=WTVooKLLVT8 I think the variable should be volatile, says there is hardly any over head.

like image 112
tgkprog Avatar answered Nov 09 '22 07:11

tgkprog


Am I correct that the exchange method performs memory synchronization, just as much as using a Lock?

You are right. The javadoc specifies there is a happen-before relation:

"Memory consistency effects: For each pair of threads that successfully exchange objects via an Exchanger, actions prior to the exchange() in each thread happen-before those subsequent to a return from the corresponding exchange() in the other thread."

What do you think is the best solution?

Both are equivalent. You should target expressiveness. I find the synchronisation/Lock/Monitor based solution more expressive than the exchanged based one. But it does not really matter if you abstract this code in a dedicated class.

Am I doing it right, or do I miss another obvious solution?

AFAIK no If you don't want to re-implement the wheel.

Please note that your ReentrantLock based solution can also be written using plain old synchronisation or Monitor from Guava.

See: http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/Monitor.html for a comparison.

And I didn't find that the CyclicBarrier was making the code simpler... (actually I didn't totally understand how to use it, but it didn't look simpler than using

CyclicBarrier does not fit your needs. It is not designed for mutual exclusion; it allows a set of threads to define a common barrier. Threads will execute concurrently and wait for each other at some point before moving to the next step.

like image 26
Clément MATHIEU Avatar answered Nov 09 '22 08:11

Clément MATHIEU