The title description is as follows:
Write A program, open three threads, the ID of the three threads are respectively A, B and C, each thread to print their OWN ID on the screen 10 times, requiring the output results must be displayed in ABC order, such as ABCABCABC… In turn, recursive
This is a classic multithreaded programming interview question, first of all, the demand of this question is very strange, first open multithreaded, and then serial print ABC, this is not full of support? But since it’s an interview question, I’ll leave it at that. The purpose is to test your multithreaded programming background. This one, if you can’t write down three or four solutions, you’re embarrassed to say you learned multithreading. Ha ha joke, below for you to introduce several solutions to this problem.
1. The simplest way — use LockSupport
LockSupport is Java. The util. Concurrent. The locks under the package of tools, its static methods unpark park () () and can block the current thread respectively and awaken the effect of the specified thread, so use it to solve this problem is a piece of cake, the code is as follows:
public class PrintABC {
static Thread threadA, threadB, threadC;
public static void main(String[] args) {
threadA = new Thread(() -> {
for (int i = 0; i < 10; i++) {
// Prints the current thread name
System.out.print(Thread.currentThread().getName());
// Wake up the next thread
LockSupport.unpark(threadB);
// The current thread is blockedLockSupport.park(); }},"A");
threadB = new Thread(() -> {
for (int i = 0; i < 10; i++) {
// block and wait to wake up
LockSupport.park();
System.out.print(Thread.currentThread().getName());
// Wake up the next threadLockSupport.unpark(threadC); }},"B");
threadC = new Thread(() -> {
for (int i = 0; i < 10; i++) {
// block and wait to wake up
LockSupport.park();
System.out.print(Thread.currentThread().getName());
// Wake up the next threadLockSupport.unpark(threadA); }},"C"); threadA.start(); threadB.start(); threadC.start(); }}Copy the code
The result is as follows:
ABCABCABCABCABCABCABCABCABCABC
Process finished with exit code 0
Copy the code
2. The most traditional approach — use synchronized locks
In this method, Java synchronized keyword is directly used, and Object wait() and notifyAll() methods are used to realize the effect of alternating printing threads. However, this method is complicated and has a large amount of code. Since neither notify() nor notifyAll() can wake up the specified thread, three Boolean variables are required to control the order of thread execution. Another thing to note is that i++ in the for loop needs to be executed after the thread prints, otherwise i++ will be executed every time we wake up, whether it’s the current thread’s turn to print or not, which is obviously not what we want. The code is as follows (generally, the execution logic of THREAD B, C and thread A is similar, and only detailed comments are made in the code of thread A) :
public class PrintABC {
// Use Boolean variables to control the printing order. True indicates that the current thread is printing
private static boolean startA = true;
private static boolean startB = false;
private static boolean startC = false;
public static void main(String[] args) {
// as a lock object
final Object o = new Object();
/ / A thread
new Thread(() -> {
synchronized (o) {
for (int i = 0; i < 10;) {if (startA) {
// Indicates that it is the current thread's turn to print
System.out.print(Thread.currentThread().getName());
// B will print next, so set startB to true and others to false
startA = false;
startB = true;
startC = false;
// Wake up other threads
o.notifyAll();
// Add I here
i++;
} else {
// The current thread does not print, so continue to wait
try {
o.wait();
} catch(InterruptedException e) { e.printStackTrace(); }}}}},"A").start();
/ / thread B
new Thread(() -> {
synchronized (o) {
for (int i = 0; i < 10;) {if (startB) {
System.out.print(Thread.currentThread().getName());
startA = false;
startB = false;
startC = true;
o.notifyAll();
i++;
} else {
try {
o.wait();
} catch(InterruptedException e) { e.printStackTrace(); }}}}},"B").start();
/ / C thread
new Thread(() -> {
synchronized (o) {
for (int i = 0; i < 10;) {if (startC) {
System.out.print(Thread.currentThread().getName());
startA = true;
startB = false;
startC = false;
o.notifyAll();
i++;
} else {
try {
o.wait();
} catch(InterruptedException e) { e.printStackTrace(); }}}}},"C").start(); }}Copy the code
The result is as follows:
ABCABCABCABCABCABCABCABCABCABC
Process finished with exit code 0
Copy the code
3, use Lock and Condition implementation
Synchronized locking is a bit complicated to write, so why not ReentrantLock? This is a Java. Util. Concurrent. The locks lock under the package implementation class, it has more flexible API, can achieve finer control of multithreaded execution process, especially in the case of matching Condition using, can follow one’s inclinationsly control multiple threads of execution order, Let’s see how this combination is used in this example:
public class PrintABC {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Create three conditions using ReentrantLock's newCondition() method
// Thread A, thread B, thread C
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
/ / A thread
new Thread(() -> {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName());
// Wake up thread B
conditionB.signal();
// This thread is blocked
conditionA.await();
}
// There is a pit, remember to call signal() after the loop, otherwise the thread may stay in
// In wait state, the program cannot finish
conditionB.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// Call the unlock method in the finally blocklock.unlock(); }},"A").start();
/ / thread B
new Thread(() -> {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName());
conditionC.signal();
conditionB.await();
}
conditionC.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{ lock.unlock(); }},"B").start();
/ / C thread
new Thread(() -> {
try {
lock.lock();
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName());
conditionA.signal();
conditionC.await();
}
conditionA.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally{ lock.unlock(); }},"C").start(); }}Copy the code
The result is as follows:
ABCABCABCABCABCABCABCABCABCABC
Process finished with exit code 0
Copy the code
4, use Semaphore implementation
Semaphore (Chinese for semaphore) is an operating system concept, but there is also a semaphore class in JUC that can be used to control the number of concurrent threads. Permitting int permits Semaphore constructor with permitting int permits Semaphore constructor with permitting int permits
public Semaphore(int permits) {... }Copy the code
Permits refers to the number of permits that can be allocated to the Semaphore object. A Semaphore object in a thread can call acquire() method to enable the thread to obtain permission and continue to run. At the same time, the number of permits of the object is reduced by one. The Semaphore object calls the Release () method to release the permissions, incremented by one. Talk is cheap, show me the code!
public class PrintABC {
public static void main(String[] args) {
// If the number of initialization permissions is 1, thread A can execute first
Semaphore semaphoreA = new Semaphore(1);
// The number of initialization permissions is 0, and thread B is blocked
Semaphore semaphoreB = new Semaphore(0);
// The number of initialization permissions is 0, the C thread is blocked
Semaphore semaphoreC = new Semaphore(0);
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
// A thread is granted permission and semaphoreA is reduced to 0 for the next loop
// thread A will block until another thread executes semaphorea.release ();
semaphoreA.acquire();
// Prints the current thread name
System.out.print(Thread.currentThread().getName());
// semaphoreB license number + 1
semaphoreB.release();
} catch(InterruptedException e) { e.printStackTrace(); }}},"A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
semaphoreB.acquire();
System.out.print(Thread.currentThread().getName());
semaphoreC.release();
} catch(InterruptedException e) { e.printStackTrace(); }}},"B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
semaphoreC.acquire();
System.out.print(Thread.currentThread().getName());
semaphoreA.release();
} catch(InterruptedException e) { e.printStackTrace(); }}},"C").start(); }}Copy the code
The result is as follows:
ABCABCABCABCABCABCABCABCABCABC
Process finished with exit code 0
Copy the code
5, summary
This article introduces a total of four three thread alternate printing methods, the first method is the most simple to understand, but more can examine the multithreaded programming skills should be the second and third method, in the interview is also more points. Want to master these a few kinds of methods only and understand thoroughly, encounter this kind of question model later need not panic.