Thread Learning (20) - Print strings using multithread intervals

Suppose I now have three strings, abcdefg, 1234567,!@#$%^&, I expect to use multithreading for printing, the result is a1! b2@c3 #d4$e5%f6^g7&. This is also an interview question.

There are three scenarios, synchronized,ReentrantLock with Condition, Park of LockSupport, and unpark.

The idea for this question is what to do, use multithreading, and control the execution order of the execution threads to finish printing the results.

To control the order in which threads execute, you must use the thread's communication mechanism.

Synchronized Mode

This mechanism can be accomplished in synhronized with the wait() and notify,notifyAll methods in the lock object. However, notify can only wake up a random thread, which is definitely not useful. NotfyAll wakes all currently locked wait threads, so in order for the wake threads to be executed as I want, a conditional state is required to control them. For instance.

For example, you now define a member variable shared by threads that has three states, 0, 1, 2, and three threads A,B,C to execute data for each of these three states. My idea now is that when A is finished, B is executed, then C is executed, and then this closed-loop operation of A is executed until the final execution is completed. Then this variable, with an initial value of 0, lets it enter my A thread. After executing the operation of the A thread, set the variable to 1, call the notifyAll method, wake up the B,C threads. At this time, the B,C threads determine if the variable meets their own execution conditions, B finds that it meets, and then proceeds to the following method. C finds that it is not satisfied and continues to call the wait method to block.

Lock+Condition mode

Unlike Conditions, ReentrantLock can contain multiple Conditions, similar to the waitSet in Synchronized. Then there is such a way of thinking. There are now threads A, B, and C that poll for execution of three strings. So I can also define three conditions, which are await blocked by default. In the main thread, I first use the signal method to wake up the configuration corresponding to thread A, execute the business logic of A, complete the business logic execution of A, wake up the Condition of B in thread A, and then the B thread starts execution. Then, after B's business execution is complete, wake up the signal method in C's Condition. Execute in turn until program execution is complete.

park,unpark in LockSupport

This is similar to Lock+Condition. Lock+Condition wakes up the only thread in Condition, and park,unpark operates directly on the thread. However, there is a problem during execution. When using unpark (thread name) to wake up a thread, the thread is empty, which needs to be well understood later.

Code

package com.bo.threadstudy.four;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;

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

@Slf4j
public class ThreadPrintDataTest {
    //Multi-threaded Print Alternating Data Problem
    private static String a = "abcdefg";
    private static String b = "1234567";
    private static String c = "!@#$%^&";

    /**
     * Using Synchronized to Solve Print Data Interlacing
     */
    @Test
    public void synchronizedTest() throws InterruptedException {
        Object lock = new Object();
        char[] aArr = a.toCharArray();
        char[] bArr = b.toCharArray();
        char[] cArr = c.toCharArray();

        //What to do with this scheme? Set a status, after a printing is completed, the status is set to b, after B printing is completed, the status is set to c, after C printing is completed, the status is set to a
        //Status set to 0,1,2
        new Thread(() -> {
            for (int i = 0; i < aArr.length; i++) {
                printSynchronized(aArr, 0, i, lock);
            }
        }, "t1").start();
        new Thread(() -> {
            for (int i = 0; i < aArr.length; i++) {
                printSynchronized(bArr, 1, i, lock);
            }

        }, "t2").start();
        new Thread(() -> {
            for (int i = 0; i < aArr.length; i++) {
                printSynchronized(cArr, 2, i, lock);
            }
        }, "t3").start();

        while (true) {
            Thread.sleep(1000);
        }


    }

    private volatile Integer status = 0;

    /**
     * synchronized Polling Print Status
     *
     * @param arr       array
     * @param curStatus current state
     * @param index     index subscript
     * @param lock      Locks used
     */
    private void printSynchronized(char[] arr, int curStatus, int index, Object lock) {
        //Status should be public
        synchronized (lock) {
            //Unequal state, blocked first
            while (curStatus != status) {
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //Equal state
            System.out.print(arr[index]);
            if (status == 2) {
                status = 0;
            } else {
                status++;
            }
            lock.notifyAll();
        }
    }


    /**
     * Use Lock with Condition for use
     */
    @Test
    public void lockConditionTest() throws InterruptedException {
        //The idea is that there are multiple conditions under a Lock name, and different elements are put into different conditions to complete the operation.
        ReentrantLock lock = new ReentrantLock();
        Condition condition1 = lock.newCondition();
        Condition condition2 = lock.newCondition();
        Condition condition3 = lock.newCondition();

        char[] aArr = a.toCharArray();
        char[] bArr = b.toCharArray();
        char[] cArr = c.toCharArray();


        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < aArr.length; i++) {
                    printCondition(condition1, condition2, aArr, i, lock);
                }
            }
        }, "t1");
        t1.start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < aArr.length; i++) {
                    //The waitSet holds the thread ID, which should also be the thread ID. It must hold only one thread ID.
                    printCondition(condition2, condition3, bArr, i, lock);
                }

            }
        }, "t2").start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < aArr.length; i++) {
                    printCondition(condition3, condition1, cArr, i, lock);
                }
            }
        }, "t3").start();


        //Conditions and locks appear to be similar to synchronized calls and must be accessed in the lock method, as you can see
        lock.lock();
        try {
            condition1.signal();
        } finally {
            lock.unlock();
        }


        Thread.sleep(100000);

    }

    //The object-oriented writing by the teacher is better than that by me. I don't have an object-oriented idea, but I have the right one.
    private void printCondition(Condition curCondition, Condition nextCondition, char[] arr, int index, ReentrantLock lock) {
        lock.lock();
        try {
            //Current Condition is waiting
            curCondition.await();
            System.out.print(arr[index]);
            nextCondition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * LockSupport-based park,unpark mechanism to complete
     */
    static Thread t1;
    static Thread t2;
    static Thread t3;

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        char[] aArr = a.toCharArray();
        char[] bArr = b.toCharArray();
        char[] cArr = c.toCharArray();
        //Initially, they are suspended. External control is used to release them. Is the teacher's encapsulation mainly for the thread to initialize?

        //There is a problem with this writing, that is, waking up when the thread object is undefined, which is incorrect
        t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c1 : aArr) {
                    lockSupportPrint(c1,lock, Thread.currentThread(), t2);
                }
            }
        });


        t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c1 : bArr) {
                    lockSupportPrint(c1,lock, Thread.currentThread(), t3);
                }
            }
        });


        t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (char c1 : cArr) {
                    lockSupportPrint(c1,lock,Thread.currentThread(), t1);
                }
            }
        });
        //Thread Start Order Issue
        //TODO has a problem here. Thread variables must be defined as static variables, start() startup must be placed behind, or incoming Thread objects may be empty.
        //Go to see what's wrong later on
        t1.start();
        t2.start();
        t3.start();

        LockSupport.unpark(t1);


    }
    //No locks needed. Writing confused
    private static void lockSupportPrint(char value, ReentrantLock lock, Thread curThread, Thread nextThread){
        //Pause the current thread
        LockSupport.park();
        System.out.print(value);
        //Wake up the next thread
        LockSupport.unpark(nextThread);
    }


}

Finally, the fourth day of the course is finished, come down to prepare the fifth day of the course, except for the eighth day, there is not much left. Go on, as soon as possible.

Tags: Java Interview thread

Posted by ojsimon on Thu, 02 Jun 2022 10:51:35 +0530