Thread.join

    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(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

    public final void join() throws InterruptedException {
        join(0);
    }

join是Thread的一个方法,作用是等待线程结束,可填参数millis(等待时间),不填时,默认是0(注意0表示一直等待,大于0表示等待指定的时间).join方法会一直阻塞到指定时间,当然如果线程在指定时间之前就完成则会提前返回.
join方法还会抛出一个InterruptedException,这是当前线程被中断时抛出异常,而不是join方法属于的线程对象被中断.

如果希望在一个线程完成后再做其他操作就可以使用join

wait

wait方法是Object对象所拥有的方法,调用它会使当前线程进入等待,之后必须调用对象的notify或者notifyAll才能使线程停止等待.它也有时间参数,不填默认是0,就是一直等待.
在调用wait方法之前必须获得该对象的锁,不然会抛异常java.lang.IllegalMonitorStateException.调用notify会唤醒一个在等待的线程,notifyAll会唤起所有等待的线程

    @Test
    public void testNotify() throws Exception
    {
        Object obj = new Object();
        int capacity =10;
        Thread[] threads = new Thread[capacity];
        for (int i = 0; i <threads.length ; i++)
        {
            threads[i] = new Thread(()->
            {
                try
                {
//                    System.out.println(Thread.currentThread().getName()+" 0");
                    synchronized (obj)
                    {
//                        System.out.println(Thread.currentThread().getName()+" 1");
                        obj.wait();
//                        System.out.println(Thread.currentThread().getName()+" 2");
                    }
                    System.out.println(Thread.currentThread().getName()+" 3");
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            });
            threads[i].start();
        }
        Thread.sleep(1000);
        int beforeCount = Thread.activeCount();
        synchronized (obj)
        {
            obj.notify();
        }

        Thread.sleep(1000);
        int afterCount = Thread.activeCount();
        System.out.println("before:"+beforeCount+"\tafter:"+afterCount);
        assertEquals(beforeCount-1,afterCount);


    }

    @Test
    public void testNotifyAll() throws Exception
    {
        Object obj = new Object();
        int capacity =10;
        Thread[] threads = new Thread[capacity];
        for (int i = 0; i <threads.length ; i++)
        {
            threads[i] = new Thread(()->
            {
                try
                {
                    //                    System.out.println(Thread.currentThread().getName()+" 0");
                    synchronized (obj)
                    {
                        //                        System.out.println(Thread.currentThread().getName()+" 1");
                        obj.wait();
                        //                        System.out.println(Thread.currentThread().getName()+" 2");
                    }
                    System.out.println(Thread.currentThread().getName()+" 3");
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            });
            threads[i].start();
        }
        Thread.sleep(1000);
        int beforeCount = Thread.activeCount();
        synchronized (obj)
        {
            obj.notifyAll();
        }
        Thread.sleep(1000);
        int afterCount = Thread.activeCount();
        System.out.println("before:"+beforeCount+"\tafter:"+afterCount);
        assertEquals(beforeCount-capacity,afterCount);
    }