• 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执行完啦
    主线程结束
  • 相关阅读:
    0-完全开源的 Vue.js 入门级教程:HelloVue,发车啦!
    [Java 开源项目]一款无需写任何代码,即可一键生成前后端代码的工具
    用 Java 做个“你画手机猜”的小游戏
    PowerJob 从 0 到 1.9k star 的经历
    最后一篇:结束不想说再见,那就扬帆再次起航!
    shell 常用快捷键
    微服务网关评测对比
    Docker加入自定义iptables规则链
    CentOS.7防火墙firewalld属性
    bash和shell的差别
  • 原文地址:https://www.cnblogs.com/eoss/p/5902939.html
Copyright © 2020-2023  润新知