Java面试题:如何实现主线程等待子线程的执行

实现主线程等待子线程的几种方案

  1. 通过AQS下的CountDownLatch来实现。
  2. 通过Thread.join()方法
  3. 通过LockSupport.park()以及LockSupport.unpark()配合实现
  4. 通过Object.wait()配合Object.notify()/Obejct.notifyAll()配合实现
    基本知道这几种方式,就够了。

不同方式的关系

其中方式1和方式3都是基于AQS;AQS的各个子类实现,比如可重入锁,可重入读写锁、CountDownLatch等,都是通过LockSuppoort.park()实现线程阻塞,然后把线程加入到阻塞队列中去。然后再通过LockSupport.unpark()实现线程的唤醒;
而方式2其实底层实现是基于方式4的,我们看源码就可以看的出来;

    /**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use  wait, notify, or
     *  notifyAll on  Thread instances.
     */
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
            //这里调用了wait方法
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                //这里调用了wait方法啊
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

使用wait和notify的简单实现

下面是一个简单的示例:
我们创建一个了子线程son,子线程会将传入其中的线程唤醒;
而主线程main中,我们先启动子线程son,并将main作为参数传入其中,最后再阻塞自己;
这样,就可以依靠子线程son,唤醒主线程main。

public class WaitNotifyTest extends Thread{

	//私有参数1:传入的线程,将会被唤醒
    private Thread mainThread;

	//同步监视器,其实相当于一个锁
    private Object object;

	//构造方法
    WaitNotifyTest(Thread mainThread, Object o){
        this.mainThread = mainThread;
        this.object = o;
    }

    @Override
    public void run(){
    	//子线程获取同步监视器的锁;该操作基于主线程释放锁,否则子线程无法获取锁
        synchronized(object){
            System.out.println("子线程开始执行");
            try {
            	//子线程等待一会,让主线程阻塞的操作生效	
                Thread.sleep(4000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            //唤醒被同步监视器阻塞住的所有线程
            object.notifyAll();
            System.out.println("主线程被唤醒");
        }
    }


    public static void main(String[] args) {
    	//创建一个同步监视器
        Object lock = new Object();
        //将同步监视器和主线程传入子线程中
        WaitNotifyTest waitNotifyTest = new WaitNotifyTest(Thread.currentThread(),lock);
        //主线程先获取锁,
        synchronized (lock){
            System.out.println("主线程被阻塞");
            //主线程启动子线程
            waitNotifyTest.start();
            System.out.println("启动子线程");
            try {
            	//主线程阻塞,并且wait()操作会使当前线程释放获取的锁
            	//从而子线程可以获取锁,执行同步方法
                lock.wait();
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
        System.out.println("主线程执行完成");
    }
}

wait和park的异别

  1. wait和park都会将线程阻塞,但是wait方法会释放锁,park方法则不会;
  2. wait方法阻塞的线程和park方法阻塞线程都可以响应中断,但wait阻塞线程会抛出InterruptedException,而park方法则不会抛出异常。
  3. wait和notify/notifyAll需要保证先后顺序,比如示例中,先调用的是wait()再调用的是notify,这样主线程才能被正常唤醒;如果先调用notify,再调用wait,主线程会一直阻塞。而park和unpark则不需要保证先后顺序。原因在于,park实现的是一个将许可证+1,unpark是将许可证-1,只要保证最终是0,线程即可正常运行。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值