Java multithreading — let the main thread wait for the child thread to finish executing
Reference link: https://www.cnblogs.com/eoss/p/5902939.html
When using java multithreading programming, it is often encountered that the main thread needs to wait for the execution of the child thread to continue. Next, we introduce a simple way to make the main thread wait.
java. util. concurrent. CountDownLatch
Use countdownlatch Await () method is very simple to complete the waiting of the main thread:
public class ThreadWait { public static void main(String[] args) throws InterruptedException { int threadNumber = 10; final CountDownLatch countDownLatch = new CountDownLatch(threadNumber); for (int i = 0; i < threadNumber; i++) { final int threadID = i; new Thread() { public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(String.format("threadID:[%s] finished!!", threadID)); countDownLatch.countDown(); } }.start(); } countDownLatch.await(); System.out.println("main thread finished!!"); } }
Countdownlatch source code analysis:
1. First look at the await() method:
public void await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); } private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean Failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC Failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (Failed) cancelAcquire(node); } }
We mainly focus on the parkandcheckinterrupt() method, which is how to block the main thread:
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); //通过LockSupport.park()方法将线程交给系统阻塞; return Thread.interrupted(); }
2. Look at the countdown () method. Let's look at the unparksuccess () method finally called by countdown;
private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ int ws = node.waitStatus; if (ws < 0) compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); }
We can see that locksupport The unpark () method wakes up the main thread.
Note: the park and unpark methods in locksupport class are native methods in unsafe;
Finally, let's take a look at the simplest code that blocks the wake-up thread using the park and unpark methods:
public static void main(String[] args) { Thread t = new Thread(() -> { System.out.println("阻塞线程1"); LockSupport.park(); System.out.println("线程1执行完啦"); }); t.start(); try { Thread.sleep(2000); System.out.println("唤醒线程1"); LockSupport.unpark(t); Thread.sleep(2000); System.out.println("主线程结束"); } catch (InterruptedException e) { e.printStackTrace(); } }
Operation results:
阻塞线程1 唤醒线程1 线程1执行完啦 主线程结束