A programming problem is as follows:
Instantiate three threads, one thread prints a, one prints b, one prints c, three threads execute at the same time, and require six connected ABCs to be printed.
Topic analysis:
From the meaning of the question, we can conclude that we need to use three threads, three threads will print six characters, the key is how to ensure that the order must be abc.. So this problem needs synchronization mechanism to solve the problem!
Let the thread of printing character A be ThreadA, ThreadB of printing B and ThreadC of printing C. The problem is the synchronous wake-up operation among threads. The main purpose is to make the program press ThreadA - > ThreadB - > ThreadC.-
> ThreadA loop executes three threads, so I sorted out three ways to solve this problem.
1. Through two locks (not recommended, poor readability and security)
/** * Continuous printing of abcabc is realized based on two lock s. * @author fhr * @since 2017/09/04 */ public class TwoLockPrinter { @Test public void test() throws InterruptedException { // Printing A Thread lock Object lockA = new Object(); // Printing B Thread lock Object lockB = new Object(); // Printing C Thread lock Object lockC = new Object(); ThreadGroup group = new ThreadGroup("xx"); // Printing a Threads Thread threadA = new Thread(group, new Printer(lockC, lockA, 'A')); // Printing b Threads Thread threadB = new Thread(group, new Printer(lockA, lockB, 'B')); // Printing c Threads Thread threadC = new Thread(group, new Printer(lockB, lockC, 'C')); // Turn on a b c thread threadA.start(); Thread.sleep(100); threadB.start(); Thread.sleep(100); threadC.start(); // Main Thread Loop Delivery cpu Usufruct while (group.activeCount() > 0) { Thread.yield(); } } // Print thread private class Printer implements Runnable { // Printing times private static final int PRINT_COUNT = 6; // Print locks for the previous thread private final Object fontLock; // Print lock for this thread private final Object thisLock; // Printing character private final char printChar; public Printer(Object fontLock, Object thisLock, char printChar) { super(); this.fontLock = fontLock; this.thisLock = thisLock; this.printChar = printChar; } @Override public void run() { // Continuous printing PRINT_COUNT second for (int i = 0; i < PRINT_COUNT; i++) { // Get the print lock of the previous thread synchronized (fontLock) { // Get the print lock for this thread synchronized (thisLock) { // Printing character System.out.print(printChar); // Wake up the following thread through the print lock of this thread // notify and notifyall All possible,Because there is only one thread waiting at the same time. thisLock.notify(); // Not for the last time fontLock Waiting to be awakened // It must be judged, or it can be printed six times, but after six times it will be deadlocked directly. if (i < PRINT_COUNT - 1) { try { // adopt fontLock Waiting to be awakened fontLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } } }
In order to determine the order of wake-up and wait, each thread must hold two object locks at the same time in order to continue execution. One object lock is fontLock, which is the object lock held by the previous thread, and the other is itself.
Object lock thisLock. The main idea is that in order to control the order of execution, fontLock locks must be held first, that is, the former thread should release the object locks of the former thread itself, and the current thread should apply for its own object locks.
After that, it first calls thisLock.notify() to release its own object lock, wake up the next waiting thread, and then calls fontLock.wait() to release the prev object lock, pause the current thread, and wait to be waked up again before entering the loop. transport
Line the above code, you can find three threads printing ABC circularly, a total of six times. The main process of program running is that thread A runs first, holds C and A object locks, then releases lock A and wakes up B. Thread B waits for lock A, applies for lock B, and then types
Print B, release B lock, wake up C, thread C waits for B lock, then apply for C lock, print C, then release C lock, wake up A. It doesn't seem to be a problem, but if you think about it carefully, you'll find that the problem is the initial bar.
Three threads start in the order of A, B and C. According to the previous thinking, A wakes B, B wakes C, C wakes A again. But this assumption depends on the order in which threads are scheduled and executed in the JVM, so they need to be controlled manually.
Start order, Thread.Sleep(100)
2. Through a ReentrantLock and three conditon s (recommendation, security, performance and readability are high)
/** * Continuous printing of abcabc based on one ReentrantLock and three conditon s is realized. * @author fhr * @since 2017/09/04 */ public class RcSyncPrinter { @Test public void test() throws InterruptedException { ThreadGroup group = new ThreadGroup("xx"); // Write lock ReentrantLock lock = new ReentrantLock(); // Printing a Thread condition Condition conditionA = lock.newCondition(); // Printing b Thread condition Condition conditionB = lock.newCondition(); // Printing c Thread condition Condition conditionC = lock.newCondition(); // instantiation A thread Thread printerA = new Thread(group, new Printer(lock, conditionA, conditionB, 'A')); // instantiation B thread Thread printerB = new Thread(group, new Printer(lock, conditionB, conditionC, 'B')); // instantiation C thread Thread printerC = new Thread(group, new Printer(lock, conditionC, conditionA, 'C')); // Start in turn A B C thread printerA.start(); Thread.sleep(100); printerB.start(); Thread.sleep(100); printerC.start(); // Main Thread Loop Delivery CPU Usufruct while (group.activeCount() > 0) { Thread.yield(); } } // Print thread private class Printer implements Runnable { // Printing times private static final int PRINT_COUNT = 6; // Print lock private final ReentrantLock reentrantLock; // What this thread needs to print condition private final Condition thisCondtion; // Next thread printing required condition private final Condition nextCondtion; // Printing character private final char printChar; public Printer(ReentrantLock reentrantLock, Condition thisCondtion, Condition nextCondition, char printChar) { this.reentrantLock = reentrantLock; this.nextCondtion = nextCondition; this.thisCondtion = thisCondtion; this.printChar = printChar; } @Override public void run() { // Get Print Lock into Critical Zone reentrantLock.lock(); try { // Continuous printing PRINT_COUNT second for (int i = 0; i < PRINT_COUNT; i++) { System.out.print(printChar); // Use nextCondition Wake up the next thread // Because there is only one thread waiting, so signal perhaps signalAll Fine nextCondtion.signal(); // Not for the last time thisCondtion Waiting to be awakened // It must be judged, or it can be printed six times, but after six times it will be deadlocked directly. if (i < PRINT_COUNT - 1) { try { // This thread releases the lock and waits for wake-up thisCondtion.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } } finally { // Release Print Lock reentrantLock.unlock(); } } } }
Consider this question carefully. Since only one thread can print characters at the same time, why don't we use a synchronous lock ReentrantLock? Wake-up operations between threads can be implemented through Conditions, and Conditions can have
Multiple, each condition.await block can only be awakened by the condition's signal/signalall! This is not achieved by the synchronized keyword, so we can give each print thread its own condition and the following
Each time a character is printed, the condition.signal of the next thread is called to wake up the next thread, and then it releases the lock itself through its condition.await and waits to wake up.
3. Implement by a lock and a state variable (recommended)
/** * Achieve continuous printing of abcabc based on a lock and a state variable. * @author fhr * @since 2017/09/04 */ public class StateLockPrinter { //state variable private volatile int state=0; @Test public void test() throws InterruptedException { //lock Object lock=new Object(); ThreadGroup group=new ThreadGroup("xx"); //Printing A Threads Thread threadA=new Thread(group,new Printer(lock, 0,1, 'A')); //Printing B Threads Thread threadB=new Thread(group,new Printer(lock, 1,2, 'B')); //Printing C Threads Thread threadC=new Thread(group,new Printer(lock, 2,0, 'C')); //One start A B C thread threadA.start(); Thread.sleep(1000); threadB.start(); Thread.sleep(1000); threadC.start(); //Loop checks the number of live threads in a thread combination while (group.activeCount()>0) { //Give up CPU Usufruct Thread.yield(); } } //Print thread private class Printer implements Runnable{ //Printing times private static final int PRINT_COUNT=6; //Print lock private final Object printLock; //Print label and state Variable correlation private final int printFlag; //Print flags for threads of successor threads, state Variable correlation private final int nextPrintFlag; //Print characters for this thread private final char printChar; public Printer(Object printLock, int printFlag,int nextPrintFlag, char printChar) { super(); this.printLock = printLock; this.printFlag=printFlag; this.nextPrintFlag=nextPrintFlag; this.printChar = printChar; } @Override public void run() { //Get Print Lock into Critical Zone synchronized (printLock) { //Continuous printing PRINT_COUNT second for(int i=0;i<PRINT_COUNT;i++){ //Cyclic checkpoint blocks every time and waits for wake-up while (state!=printFlag) { try { printLock.wait(); } catch (InterruptedException e) { return; } } //Printing character System.out.print(printChar); //Set the status variable as the flag bit for the next thread state=nextPrintFlag; //Pay attention to notifyall,Otherwise it will be deadlocked because notify Notify only one. //But there are two waiting at the same time.,If the wake-up is not correct, no one will wake up, deadlock. printLock.notifyAll(); } } } } }
The state variable is an integral variable of volatile, 0 represents print a,1 represents print b,2 represents print c. All three threads check the flag bit circularly. By judging before and after blocking, the correct order of current printing can be ensured, and then the threads.
Print the character, then set the next status character, wake up other threads, and re-enter the loop.
Supplementary questions
Three Java multithreaded loops print incremental numbers, each thread prints five values, printing cycle 1-75, the same solution:
/** * Digital printing. Three threads print numbers at the same time. * The first thread prints 12345, and the second thread prints 678910... * @author fhr * @since 2017/09/04 */ public class NumberPrinter { //print counter private final AtomicInteger counter=new AtomicInteger(0); @Test public void test() throws InterruptedException { //Print lock ReentrantLock reentrantLock=new ReentrantLock(); //Printing A Thread Condition Condition conditionA=reentrantLock.newCondition(); //Printing B Thread Condition Condition conditionB=reentrantLock.newCondition(); //Printing C Thread Condition Condition conditionC=reentrantLock.newCondition(); ThreadGroup group=new ThreadGroup("xx"); //Print thread A Thread threadA=new Thread(group,new Printer(reentrantLock,conditionA, conditionB)); //Print thread B Thread threadB=new Thread(group,new Printer(reentrantLock, conditionB, conditionC)); //Print thread C Thread threadC=new Thread(group,new Printer(reentrantLock, conditionC, conditionA)); // Turn on a b c thread threadA.start(); Thread.sleep(100); threadB.start(); Thread.sleep(100); threadC.start(); while (group.activeCount()>0) { Thread.yield(); } } private class Printer implements Runnable{ //Total Printing Requirements TOTAL_PRINT_COUNT second private static final int TOTAL_PRINT_COUNT=5; //Each time printing PER_PRINT_COUNT second private static final int PER_PRINT_COUNT=5; //Print lock private final ReentrantLock reentrantLock; //Of the previous thread condition private final Condition afterCondition; //This thread condition private final Condition thisCondtion; public Printer(ReentrantLock reentrantLock, Condition thisCondtion,Condition afterCondition) { super(); this.reentrantLock = reentrantLock; this.afterCondition = afterCondition; this.thisCondtion = thisCondtion; } @Override public void run() { //Enter the critical zone reentrantLock.lock(); try { //Loop printing TOTAL_PRINT_COUNT second for(int i=0;i<TOTAL_PRINT_COUNT;i++){ //Print operation for(int j=0;j<PER_PRINT_COUNT;j++){ System.out.println(counter.incrementAndGet()); } //adopt afterCondition Notify the subsequent thread afterCondition.signalAll(); if(i<=TOTAL_PRINT_COUNT-1){ try { //This thread releases the lock and waits for wake-up thisCondtion.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } } finally { reentrantLock.unlock(); } } } }