zoukankan      html  css  js  c++  java
  • Java多线程--让主线程等待子线程执行完毕

    使用Java多线程编程时经常遇到主线程需要等待子线程执行完成以后才能继续执行,那么接下来介绍一种简单的方式使主线程等待。

    java.util.concurrent.CountDownLatch

    使用countDownLatch.await()方法非常简单的完成主线程的等待:

    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源码解析:

    1.先看看await()方法:

        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);
            }
        }

    主要看parkAndCheckInterrupt()方法,就是是如何将主线程阻塞住的方法:

        private final boolean parkAndCheckInterrupt() {
            LockSupport.park(this);    //通过LockSupport.park()方法将线程交给系统阻塞;
            return Thread.interrupted();
        }

    2.看看countDown()方法,我们看看最终被countDown调用的unparkSuccessor()方法;

        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);
        }

    我们可以看到最终使用LockSupport.unpark()方法唤醒了主线程。

    注:LockSupport类中的park与unpark方法都是使用的unsafe中的native本地方法;

    最后我们来看一段最简单的使用park与unpark方法阻塞唤醒线程代码:

        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();
            }
    
        }

    运行结果:

    阻塞线程1
    唤醒线程1
    线程1执行完啦
    主线程结束
  • 相关阅读:
    大道至简读后感
    机器学习十讲(一)
    第一个TensorFlow的简单例子
    初识深度学习
    如何使用本地的Navicat连接服务器中的Mysql
    阿里云ECS-安装Tomcat
    阿里云ECS-CentOS 8上安装MySQL 8.0
    阿里云ECS--CentOS8安装jdk1.8
    进度报告十(重大技术需求)
    进度报告九 (重大技术需求调研)
  • 原文地址:https://www.cnblogs.com/eoss/p/5902939.html
Copyright © 2011-2022 走看看