background

A few days ago, a group of friends asked me a multi-threaded topic encountered in the interview of Ali, this topic is more interesting, here to share with you.

Without further ado, let’s get straight to the subject:

For example, if N=3, the output is thread0:0 thread1:1 thread2:2 thread0:3 thread1:4.....Copy the code

Some of the friends who often brush interview questions, must have encountered the following question before:

Two threads print odd or even numbers of 0 to 100 alternately: Even thread: 0 Odd thread: 1 Even thread: 2 Odd thread: 3Copy the code

These two problems look similar, but the second problem is a little bit easier, and you can think about how to print two odd even threads.

Two threads odd even print

Some of you might do the neat thing here, take a single thread through the loop, and do the odd or even check every time in the loop, and then print out the result that we want. We shall not dwell on this violation of the title here.

In fact, to do this problem, we need to control the order in which the two threads execute, so even threads execute and then odd threads execute, and it’s kind of like a notification mechanism, where even threads notify odd threads, and odd threads notify even threads. When you see notifications/waits, you immediately think of wait and notify in Object. Here we implement this with wait and notify as follows:

{static class SoulutionTask implements Runnable{static int value = 0; @Override public voidrun() {
            while (value <= 100){
                synchronized (SoulutionTask.class){
                    System.out.println(Thread.currentThread().getName() + ":" + value++);
                    SoulutionTask.class.notify();
                    try {
                        SoulutionTask.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    public static void main(String[] args) {
        new Thread(new SoulutionTask(), "Even").start();
        new Thread(new SoulutionTask(), "Odd").start(); }}Copy the code

Here we have two threads that use notify and wait to control the execution of our thread to print out the result of our goal

N threads print circularly

Back to our original problem again, N threads circulation print, solved the problem I’ll help group of friend after, again cast the problem in the group of inside out, many old drivers before seen alternating print, even this topic, then immediately made several versions, let’s take a look at the old driver 1 code:

Public implements Runnable {private static final Object LOCK = new Object(); Private static int current = 0; private static int current = 0; /** * private int threadNo; /** * private int threadCount; /** * private int maxInt; Public old driver 1(int threadNo, int threadCount, int maxInt) {this.threadNo = threadNo; this.threadCount = threadCount; this.maxInt = maxInt; } @Override public voidrun() {
        while (true) {synchronized (LOCK) {// determine whether it is the current thread's turn to executewhile(current % threadCount ! = threadNo) {if (current > maxInt) {
                        break; } try {// If not, the current thread enterswaitLOCK.wait(); } catch (Exception e) { e.printStackTrace(); }} // The maximum value breaks out of the loopif (current > maxInt) {
                    break;
                }
                System.out.println("thread" + threadNo + ":"+ current); current++; // Wake up the otherswaitThread LOCK. NotifyAll (); } } } public static void main(String[] args) { int threadCount = 3; int max = 100;for(int i = 0; i < threadCount; I ++) {new Thread(new thread1 (I, threadCount, Max)).start(); }}}Copy the code

In run, we change the notify to notifyAll. NotifyAll means that all wait threads execute notifyAll, which is incorrect. In this case, only a thread in wait is released from the current wait state, also known as wake up, because the awakened thread is wrapped in a synchronized block.

The old driver’s code did work, but what was the problem? When we threads is very big, because we are not sure wake up the thread to the end is the next to perform may appear in the lock but not their implementation, and then enter the wait, now has 100 threads, for example, is now the first thread in execution, need a second thread execution after he performed, But the 100th thread grabs it, realizes it’s not itself and enters wait, then the 99th thread grabs it, realizes it’s not itself and enters wait, then 98,97… Until the third thread grabs the lock, and the second thread finally grabs the lock, there will be a lot more work done for nothing, even though it will accomplish the goal in the end.

There are other old drivers with lock/condition also implemented such a function, there are old drivers with a relatively novel method such as queue to do, of course, I will not mention here, the general principle is based on the above, here I say my approach, in Java multithreading provides some common synchronizer, Semaphore, or Semaphore, is a good use for this scenario. We have the previous thread holding the next thread’s Semaphore, and we associate it all with an array of Semaphore arrays, as follows:

static int result = 0;
    public static void main(String[] args) throws InterruptedException {
        int N = 3;
        Thread[] threads = new Thread[N];
        final Semaphore[] syncObjects = new Semaphore[N];
        for (int i = 0; i < N; i++) {
            syncObjects[i] = new Semaphore(1);
            if (i != N-1){
                syncObjects[i].acquire();
            }
        }
        for (int i = 0; i < N; i++) {
            final Semaphore lastSemphore = i == 0 ? syncObjects[N - 1] : syncObjects[i - 1];
            final Semaphore curSemphore = syncObjects[i];
            final int index = i;
            threads[i] = new Thread(new Runnable() {

                public void run() {
                    try {
                        while (true) {
                            lastSemphore.acquire();
                            System.out.println("thread" + index + ":" + result++);
                            if(result > 100){ System.exit(0); } curSemphore.release(); } } catch (Exception e) { e.printStackTrace(); }}}); threads[i].start(); }}Copy the code

In this way, we will not have a waste of threads wake up, each thread in accordance with the order we agreed to execute, this is actually the interviewer needs to test the place, so that the execution of each thread can be controlled in your hands, this can also verify whether your multi-threading knowledge is firm.

Finally, this article was included in the JGrowing-Java interview, which is a comprehensive and excellent Java learning path jointly built by the community. If you want to participate in the maintenance of open source projects, you can build it together. Making the address is: https://github.com/javagrowing/JGrowing trouble yo give a little star.

If you find this article helpful to you, your attention and forwarding will be my biggest support.