Two threads print alternately

Posted by LuciBKen on Sat, 18 Dec 2021 21:44:40 +0100

Title: start two threads to print odd and even numbers from 0 to 100 alternately

You may have encountered this problem during the interview. Although you have learned the knowledge related to multithreading, you may not be able to write it for a while and a half. Now let me take you to write it again!

Method 1

First, we can observe that odd and even numbers are printed, so we can write code through this feature. If we have a global variable count, when count is equal to an even number, we will let thread 1 print. When count is equal to an odd number, we will let thread 2 print. So how? Here we can use locks to ensure that only one thread is executing at the same time. However, if the same thread always grabs the lock and the other thread doesn't get it, it will cause the thread to do a lot of meaningless idling, which is very inefficient and can't be favored by the interviewer.

The code is as follows:

public class Test {

    // Global variable count
    private int count = 0;

    // lock
    private final Object lock = new Object();

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

    public void turning() {
        Thread even = new Thread(() -> {
            while (count < 100) {
                // Acquire lock
                synchronized (lock) {
                    // Only even numbers are processed
                    if ((count & 1) == 0) {
                        System.out.println(Thread.currentThread().getName() + ": " + count++);
                    }
                }
            }
        }, "even numbers");
        Thread odd = new Thread(() -> {
            while (count < 100) {
                // Acquire lock
                synchronized (lock) {
                    // Only odd numbers are processed
                    if ((count & 1) == 1) {
                        System.out.println(Thread.currentThread().getName() + ": " + count++);
                    }
                }
            }
        }, "Odd number");
        even.start();
        odd.start();
    }
}

Method 2

Idea: the principle of this implementation is that thread 1 wakes up other threads after printing, then gives up the lock and enters the sleep state. Because when you enter the sleep state, you will not grab the lock with other threads. At this time, only thread 2 is acquiring the lock, so thread 2 is bound to get the lock. Thread 2 executes with the same logic, wakes up thread 1, gives up the lock it holds, and enters the sleep state. This goes back and forth and continues until the task is completed. This achieves the effect of two threads obtaining locks alternately.

The code is as follows:

public class Test {

    // Global variable count
    private int count = 0;

    // lock
    private final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Test test = new Test();
        test.turning();
    }

    public void turning() throws InterruptedException {
        new Thread(new TurningRunner(), "even numbers").start();
        // Ensure that even threads acquire locks first
        Thread.sleep(1);
        new Thread(new TurningRunner(), "Odd number").start();
    }

    class TurningRunner implements Runnable {
        @Override
        public void run() {
            while (count <= 100) {
                // Acquire lock
                synchronized (lock) {
                    // Print when you get the lock
                    System.out.println(Thread.currentThread().getName() + ": " + count++);
                    // Wake up other threads
                    lock.notifyAll();
                    try {
                        if (count <= 100) {
                            // If the task is not finished, release the current lock and sleep
                            lock.wait();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

Mode III

Semaphore implementation

The code is as follows:

public class PrintNum {
    private int n;
    private Semaphore odd = new Semaphore(1);
    private Semaphore even = new Semaphore(0);

    public PrintNum(int n) {
        this.n = n;
    }

    public void printOdd() {
        for (int i=1; i<=n; i+=2) {
            try {
                odd.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Odd number" + i);
            even.release();
        }
    }

    public void printEven() {
        for(int i=2; i<=n; i+=2) {
            try {
                even.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("even numbers" + i);
            odd.release();
        }
    }

    public static void main(String[] args){
        PrintNum printNum = new PrintNum(100);
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(() -> {
            printNum.printOdd();
        });
        executorService.submit(() -> {
            printNum.printEven();
        });
        executorService.shutdown();
    }
}

Tips:

acquire(): acquire a license

release(): release the license

Mode 4

Using the continuation implementation, let's look at the code. The code is more intuitive

The code is as follows:

public class PrintNumThreadTest {

    public static void main(String[] args) {
        PrintNumOpt printNumOpt = new PrintNumOpt();
        new Thread(() -> printNumOpt.print0()).start();
        new Thread(() -> printNumOpt.print1()).start();
    }
}

class PrintNumOpt {

    int num = 0;
    int opt = 0;
    int maxNum = 100;

    ReentrantLock lock = new ReentrantLock();
    
    /**
     * even numbers
     */
    Condition condition0 = lock.newCondition();
    
    /**
     * Odd number
     */
    Condition condition1 = lock.newCondition();

    public void print0() {
        while (this.opt == 0) {
            lock.lock();
            try {
                for (; num < maxNum; num++) {
                    if (num % 2 == 0) {
                        System.out.println("even numbers:" + num);
                        this.opt = 1;
                        condition1.signal();
                        condition0.await();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public void print1() {
        while (this.opt == 1) {
            lock.lock();
            try {
                for (; num < maxNum; num++) {
                    if (num % 2 == 1) {
                        System.out.println("Odd:" + num);
                        this.opt = 0;
                        condition0.signal();
                        condition1.await();
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }
}

Tips:

public void await() causes the current thread to wait until a signal or interrupt signal is issued

public void signal() wakes up a waiting thread

public void signalAll() wakes up all waiting threads

extend

Title: three threads print 1, 2 and 3 alternately; 4,5,6…

Let's think about it first. I have time to write an article 🤣🤣🤣

quote:

https://blog.csdn.net/dadiyang/article/details/88315124

https://www.jianshu.com/p/72914f43a19f

https://www.cxymm.net/article/bobo1356/103408137

Topics: Java Interview