“This is the 9th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”

Hello, I’m looking at the mountains.

Here’s an interview question to practice your skills. This question has been asked in Ali and Millet, so put it in this, hoping to get a better answer.

Three threads A, B and C are implemented. Thread A continuously prints “A”, thread B continuously prints “B”, and thread C continuously prints “C”. The startup sequence is thread C, thread B, and thread A, and the printing result is ABC.

Solution one: state bit variable control

This problem examines the sequential execution of multiple threads. That is, the first thread is the first to meet the condition and start executing, and when it finishes, the second thread meets the condition and starts executing, and so on. As you can imagine, multiple threads spin to wait for the state bits to change to indicate the conditions under which the thread is executing.

Online Code:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ABCThread {
    private static final Lock lock = new ReentrantLock();
    private static volatile int state = 0;

    private static final Thread threadA = new Thread(() -> {
        while (true) {
            lock.lock();
            try {
                if (state % 3= =0) {
                    System.out.println("A");
                    state++;
                    break;
                } else {
                    System.out.println("A thread & state = "+ state); }}finally{ lock.unlock(); }}});private static final Thread threadB = new Thread(() -> {
        while (true) {
            lock.lock();
            try {
                if (state % 3= =1) {
                    System.out.println("B");
                    state++;
                    break;
                } else {
                    System.out.println("B thread & state = "+ state); }}finally{ lock.unlock(); }}});private static final Thread threadC = new Thread(() -> {
        while (true) {
            lock.lock();
            try {
                if (state % 3= =2) {
                    System.out.println("C");
                    state++;
                    break;
                } else {
                    System.out.println("C thread & state = "+ state); }}finally{ lock.unlock(); }}});public static void main(String[] args) { threadC.start(); threadB.start(); threadA.start(); }}Copy the code

As you can see, the state bit is volatile in the hope that when one thread changes the value of the state bit, other threads can read it. This is within the scope of the Java memory model, as described in a separate section.

This will solve the problem, but there is a lot of performance cost. Println (“A thread & state = “+ state); system.out.println (“A thread & state =” + state); , we can run it to see the result:

C thread & state = 0 ... 67 line C thread & state = 0 thread & state = 0... C thread&state = 1 C thread&state = 1 C thread&state = 1 C thread&state = 1 C thread & state = 1 B CCopy the code

As you can see, thread C and thread B spin more than 100 times before thread A gets the lock and prints. Then thread C spins another 53 times before thread B acquires the lock. Performance loss can be seen.

Solution 2: Condition Realizes the Condition judgment

Since unconditional spin wastes performance, add conditional spin.

The code is as follows:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ABCThread2 {
    private static final Lock lock = new ReentrantLock();
    private static volatile int state = 0;
    private static final Condition conditionA = lock.newCondition();
    private static final Condition conditionB = lock.newCondition();
    private static final Condition conditionC = lock.newCondition();

    private static final Thread threadA = new Thread(() -> {
        while (true) {
            lock.lock();
            try {
                while(state % 3! =0) {
                    System.out.println("A await start");
                    conditionA.await();
                    System.out.println("A await end");
                }
                System.out.println("A");
                state++;
                conditionB.signal();
                break;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{ lock.unlock(); }}});private static final Thread threadB = new Thread(() -> {
        while (true) {
            lock.lock();
            try {
                while(state % 3! =1) {
                    System.out.println("B await start");
                    conditionB.await();
                    System.out.println("B await end");
                }
                System.out.println("B");
                state++;
                conditionC.signal();
                break;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{ lock.unlock(); }}});private static final Thread threadC = new Thread(() -> {
        while (true) {
            lock.lock();
            try {
                while(state % 3! =2) {
                    System.out.println("C await start");
                    conditionC.await();
                    System.out.println("C await end");
                }
                System.out.println("C");
                state++;
                break;
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{ lock.unlock(); }}});public static void main(String[] args) { threadC.start(); threadB.start(); threadA.start(); }}Copy the code

Conditional spin is realized by Condition of Lock Lock, and the running results are as follows:

C await start
B await start
A
B await end
B
C await end
C

Copy the code

Condition.await (); condition.await (); Release the lock and then wait for the condition to wake up to regain the lock. Then thread B, and finally thread A starts executing, finds that it meets the condition, runs directly, and then wakes up thread B’s lock condition, and so on. This is actually very similar to semaphores.

Solution three: semaphore

Code first:

import java.util.concurrent.Semaphore;

class ABCThread3 {
    private static Semaphore semaphoreA = new Semaphore(1);
    private static Semaphore semaphoreB = new Semaphore(1);
    private static Semaphore semaphoreC = new Semaphore(1);

    private static final Thread threadA = new Thread(() -> {
        try {
            semaphoreA.acquire();
            System.out.println("A");
            semaphoreB.release();
        } catch(InterruptedException e) { e.printStackTrace(); }});private static final Thread threadB = new Thread(() -> {
        try {
            semaphoreB.acquire();
            System.out.println("B");
            semaphoreC.release();
        } catch(InterruptedException e) { e.printStackTrace(); }});private static final Thread threadC = new Thread(() -> {
        try {
            semaphoreC.acquire();
            System.out.println("C");
        } catch(InterruptedException e) { e.printStackTrace(); }});public static void main(String[] args) throws InterruptedException { semaphoreB.acquire(); semaphoreC.acquire(); threadC.start(); threadB.start(); threadA.start(); }}Copy the code

Semaphoreb.acquire (); semaphoreb.acquire (); And semaphoreC. Acquire (); In order to release the signals of B and C, the semaphore acquisition in thread B and thread C can be blocked until the signal value is obtained sequentially.

At the end of the article to summarize

All three methods can be used to solve the problem, but there are differences in code complexity and performance. Because it involves a lot of multi-threading, I will explain each point separately later.

Recommended reading

  • Java Concurrency Basics (1) : Synchronized
  • Java Concurrency Basics (2) : The main thread waits for the child thread to terminate
  • Java Concurrency basics (iii) : Talk more about CountDownLatch
  • Java Concurrency Basics (4) : CyclicBarrier
  • Java concurrency basics (5) : multithreaded sequential printing for interview practice

Hello, I’m looking at the mountains. Swim in the code, play to enjoy life. If this article is helpful to you, please like, bookmark, follow. Welcome to follow the public account “Mountain Hut”, discover a different world.