Java~Multi-threaded algorithm combat, three ways to solve LeetCode problem 1116. Print zero and odd and even numbers

Title description

Suppose there is such a class:

class ZeroEvenOdd { public ZeroEvenOdd(int n) {…} // Constructor public void zero(printNumber) {…} // Only print out 0 public void even(printNumber) {…} // Only print out even numbers public void odd( printNumber) {…} // Only print out odd numbers } The same instance of ZeroEvenOdd class will be passed to three different threads:





Thread A will call zero(), which will only output 0.
Thread B will call even(), which will only output even numbers.
Thread C will call odd(), which will only output odd numbers.
Each thread has a printNumber method to output an integer. Please modify the given code to output the integer sequence 010203040506..., where the length of the sequence must be 2n.

Example 1:

Input: n = 2
Output: "0102"
Description: Three threads execute asynchronously, one of which calls zero(), another thread calls even(), and the last thread calls odd(). The correct output is "0102".
Example 2:

Input: n = 5
Output: "0102030405"

Source: LeetCode
Link: https://leetcode-cn.com/problems/print-zero-even-odd
Copyright is owned by LeetCode . For commercial reprints, please contact the official authorization. For non-commercial reprints, please indicate the source.

Solution 1: Use the infinite loop method

  • Idea:
    Use a state variable to determine what data should be printed at this time. If a thread is in an infinite loop, if the number printed is not this number, use the Thread.yield() method to abandon the execution at this time. If the judgment can be executed, it will jump out of the loop. , Execute the following logic. Then use the control variable to determine whether the output should be odd or even at this time.
    Note that the state should be modified with volatile to prevent the memory from being invisible.
import java.util.function.IntConsumer;

public class ZeroEvenOdd {
    
    
    private int n;
    private volatile int state = 0;
    private volatile boolean control = true;

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

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
    
    

        for (int i = 0; i < n; i++) {
    
    
            while (state != 0) {
    
    
                Thread.yield();
            }
            printNumber.accept(0);
            if (control) {
    
    
                state = 2;
            } else {
    
    
                state = 1;
            }
        }

    }

    public void even(IntConsumer printNumber) throws InterruptedException {
    
    
        for (int i = 2; i <= n; i += 2) {
    
    
            while (state != 2) {
    
    
                Thread.yield();
            }
            printNumber.accept(i);
            state = 0;
            control = true;
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
    
    
        for (int i = 1; i <= n; i += 2) {
    
    
            while (state != 1) {
    
    
                Thread.yield();
            }
            printNumber.accept(i);
            state = 0;
            control = false;
        }
    }
}

Solution 2: Use the semaphore mechanism

Idea:
We know that there are two key methods for semaphores, one is acquire and the other is release. These two methods are blocking. Then we implement three semaphores, using alternate consumption and release to achieve 0, odd, and even print.
When to let the thread stop execution is the key to the for loop

import java.util.concurrent.Semaphore;
import java.util.function.IntConsumer;

/**
 * 使用信号量机制可以完美解决
 * 主要俩个方法, 一个是acquire还有一个好似release
 * 最主要的还是要加一层for循环来终止线程
 */
class ZeroEvenOddBySem {
    
    
    private int n;

    private Semaphore zero = new Semaphore(1);
    private Semaphore even = new Semaphore(0);
    private Semaphore odd = new Semaphore(0);

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

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
    
    

        for (int i = 0; i < n; i++) {
    
    
            zero.acquire();
            printNumber.accept(0);
            if (i % 2 == 0) {
    
    
                odd.release();
            } else {
    
    
                even.release();
            }
        }

    }

    public void even(IntConsumer printNumber) throws InterruptedException {
    
    
        for (int i = 2; i <= n; i += 2) {
    
    
            even.acquire();
            printNumber.accept(i);
            zero.release();
        }
    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
    
    
        for (int i = 1; i <= n; i += 2) {
    
    
            odd.acquire();
            printNumber.accept(i);
            zero.release();
        }
    }
}

Solution 3: Use the lock mechanism

Idea: The
logic of the lock mechanism is more complicated in implementation, but the efficiency is higher than the above two solutions. The lock mechanism needs a state variable to determine which thread to execute. If it should not be executed, it will enter blocking and wait for wake-up. If it should be executed The execution enters the following code logic.
Obtaining odd and even numbers and stopping threads are the key to the for loop. Pay attention to this

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.IntConsumer;

public class ZeroEvenOdd {
    
    
    private int n;
    private volatile int state = 0;
    private ReentrantLock lock = new ReentrantLock();
    private Condition zero = lock.newCondition();
    private Condition even = lock.newCondition();
    private Condition odd = lock.newCondition();

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

    // printNumber.accept(x) outputs "x", where x is an integer.
    public void zero(IntConsumer printNumber) throws InterruptedException {
    
    

        for (int i = 0; i < n; i++) {
    
    
            lock.lock();
            while (state != 0) {
    
    
                zero.await();
            }
            printNumber.accept(0);

            if (i % 2 == 0) {
    
    
                state = 1;
                odd.signalAll();
            } else {
    
    
                state = 2;
                even.signalAll();
            }
            lock.unlock();
        }

    }

    public void even(IntConsumer printNumber) throws InterruptedException {
    
    

        for (int i = 2; i <= n; i += 2) {
    
    
            lock.lock();
            while (state != 2) {
    
    
                even.await();
            }
            state = 0;
            printNumber.accept(i);
            zero.signalAll();
            lock.unlock();
        }

    }

    public void odd(IntConsumer printNumber) throws InterruptedException {
    
    

        for (int i = 1; i <= n; i+= 2) {
    
    
             lock.lock();
             while (state != 1) {
    
    
                 odd.await();
             }
             state = 0;
             printNumber.accept(i);
             zero.signalAll();
             lock.unlock();
        }
    }
}

Guess you like

Origin blog.csdn.net/Shangxingya/article/details/114282613