简体   繁体   中英

How to make sure two threads printing even odd numbers maintain even first then odd order for this implementation?

I have created two runnable jobs: PrintEvenNumbersJob and PrintOddNumbersJob and spawned two threads to execute these jobs. This seems to work perfectly fine! But I smell something suspicious about this implementation. Can I have some comments and advice on this implementation?

The problem that I see with this implementation is that the program terminates only when thread1 gains the lock to the object lock first otherwise it print the odd first even second order and doesn't terminate unless I supply yet another statement "lock.notify" after for statement in PrintEvenNumbersJob (as in this implementation). My question here is how to make sure that thread1 is executed first.

public class PrintEvenNumbersJob implements Runnable {

private Object lock;

public PrintEvenNumbersJob(Object lock) {
    this.lock = lock;
}

@Override
public void run() {
    synchronized (lock) {
        for (int i = 0; i <= 10; i += 2) {

            lock.notify();

            System.out.println(i);

            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        lock.notify(); // not required if thread1 gains lock first
    }
}

}


public class PrintOddNumbersJob implements Runnable {

private Object lock;

public PrintOddNumbersJob(Object lock) {
    this.lock = lock;
}

@Override
public void run() {
    synchronized (lock) {
        for (int i = 1; i < 10; i += 2) {

            lock.notify();

            System.out.println(i);

            try {
                lock.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        lock.notify();
    }
}

}



public class EvenOddManager {

public static void main(String[] args) {

    Object lock = new Object();

    PrintEvenNumbersJob printEvenNumbersJob = new PrintEvenNumbersJob(lock);
    PrintOddNumbersJob printOddNumbersJob = new PrintOddNumbersJob(lock);

    Thread thread1 = new Thread(printEvenNumbersJob);
    Thread thread2 = new Thread(printOddNumbersJob);

    thread2.start();
    thread1.start();

}

}

Have you try using Semaphores? It's easier because you don't need to worry about the order that wait and notify are called (if you call notify before the wait, it's "lost")

Sample code:

import java.util.concurrent.*;

public class Test {

    private final Semaphore oddJobPermits = new Semaphore(0);
    private final Semaphore evenJobPermits = new Semaphore(1);

    private class EvenJob implements Runnable {
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    evenJobPermits.acquire();
                    System.out.println(i * 2);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } finally {
                    oddJobPermits.release();
                }
            }
        }
    }

    private class OddJob implements Runnable {
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    oddJobPermits.acquire();
                    System.out.println(i * 2 + 1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } finally {
                    evenJobPermits.release();
                }
            }
        }
    }

    public void run() {
        new Thread(new EvenJob()).start();
        new Thread(new OddJob()).start();
    }

    public static void main(String[] args) {
        new Test().run();
    }

}

I believe you will need a referee:

public class Referee {
    private boolean evensTurn = true;

    public void waitMyTurn(boolean even) {
        synchronized(this) {
            while (even != evensTurn) {
                try {
                    wait();
                } finally {
                }
            }
        }
    }

    public void done() {
        synchronized(this) {
            evensTurn = !evensTurn;
            notify();
        }
    }
}

public class PrintEvenNumbersJob implements Runnable {

    private Referee referee;

    public PrintEvenNumbersJob(Referee referee) {
        this.referee = referee;
    }

    @Override
    public void run() {
        for (int i = 0; i <= 10; i += 2) {

            referee.waitMyTurn(true);

            System.out.println(i);

            referee.done();
        }
    }
}

public class PrintOddNumbersJob implements Runnable {

    private Referee referee;

    public PrintOddNumbersJob(Referee referee) {
        this.referee = referee;
    }

    @Override
    public void run() {
        for (int i = 0; i <= 10; i += 2) {

            referee.waitMyTurn(false);

            System.out.println(i);

            referee.done();
        }
    }
}

I tried and tested this code. It works using Semaphore

public class TestSemaphore
{

    public static void main(String[] args)
        throws Exception
    {
        AtomicInteger count = new AtomicInteger();
        Semaphore s = new Semaphore(1, true);
        Semaphore t = new Semaphore(1, true);

        OddNumberThread oThread = new OddNumberThread(count, s, t);
        EvenNumberThread eThread = new EvenNumberThread(count, s, t);

        eThread.start();
        oThread.start();
    }

    static class EvenNumberThread
        extends Thread
    {
        private AtomicInteger count;

        private Semaphore s, t;

        public EvenNumberThread(AtomicInteger pCount, Semaphore pS, Semaphore pT)
        {
            super("Even");
            count = pCount;
            s = pS;
            t = pT;
        }

        @Override
        public void run()
        {
            // Make this thread wait until even thread starts, Order will be incorrect if removed these lines.
            s.acquireUninterruptibly();
            while (count.intValue() <= 10)
            {
                try
                {
                    // Double checking to make it work
                    s.acquireUninterruptibly();

                    System.out.println(getName() + " " + count.getAndIncrement());
                }
                finally
                {
                    t.release();
                }
            }
        }
    }

    static class OddNumberThread
        extends Thread
    {
        private AtomicInteger count;

        private Semaphore s, t;

        public OddNumberThread(AtomicInteger pCount, Semaphore pS, Semaphore pT)
        {
            super("Odd");
            count = pCount;
            s = pS;
            t = pT;
        }

        @Override
        public void run()
        {
            // Start this thread first and start printing, Order will be incorrect if removed these lines.
            t.acquireUninterruptibly();
            s.release();

            while (count.intValue() <= 10)
            {
                try
                {
                    t.acquireUninterruptibly();

                    System.out.println(getName() + " " + count.getAndIncrement());
                }
                finally
                {
                    s.release();
                }
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM