join是Thread对象里的方法.签名如下:

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

它的作用是: 当某个Thread调用join方法时,其他线程,必须要等待这个线程(即调用join的线程)运行完毕(正常执行完,或者抛出异常,但它不会影响其他线程正常执行),才能继续执行.

Talk is cheap , show me the code.

    public static void main(String[] args) throws InterruptedException {
        MyThread t1 = new MyThread();
        t1.setName("t1--thread");
        t1.start();
        t1.join();

        MyThread t2 = new MyThread();
        t2.setName("t2--thread");
        t2.start();
        t2.join();

        Thread.sleep(1 * 1000);

        MyThread t3 = new MyThread();
        t3.setName("t3--thread");
        t3.start();
        t3.join();
    }

    public static class MyThread extends Thread {
        @Override
        public void run() {
            int i = 5;
            while (i-- > 0) {
                System.out.println("in thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
//                if( i == 4 && Thread.currentThread().getName().equalsIgnoreCase("t1--thread")){
//                    throw new RuntimeException("error for t1--thread");
//                }
            }

        }
    }

它的执行结果是:

in thread t1--thread
in thread t1--thread
in thread t1--thread
in thread t1--thread
in thread t1--thread
in thread t2--thread
in thread t2--thread
in thread t2--thread
in thread t2--thread
in thread t2--thread
in thread t3--thread
in thread t3--thread
in thread t3--thread
in thread t3--thread
in thread t3--thread

Process finished with exit code 0

如果中间线程出现异常时:

in thread t1--thread
Exception in thread "t1--thread" java.lang.RuntimeException: error for t1--thread
	at com.spring.pojo.BasePojo$MyThread.run(BasePojo.java:117)
in thread t2--thread
in thread t2--thread
in thread t2--thread
in thread t2--thread
in thread t2--thread
in thread t3--thread
in thread t3--thread
in thread t3--thread
in thread t3--thread
in thread t3--thread

Process finished with exit code 0

可以看到,第一条线程的异常,并不会影响其他线程的执行.

延伸

CountDownLatch

这个是某条线程,等待 CountDownLatch 计数完毕后才执行.注意,这个计数器是不能重用的.

code:

 public static void main(String[] args) throws InterruptedException {
        final int COUNT = 3;
        CountDownLatch countDownLatch = new CountDownLatch(COUNT);

        for(int i=0; i<COUNT; i++){
            new MyThread(countDownLatch).start();
        }
        MyCountDownLatch myCountDownLatch = new MyCountDownLatch(countDownLatch);
        myCountDownLatch.start();
    }

    public static class MyCountDownLatch extends Thread{

        CountDownLatch countDownLatch;
        public MyCountDownLatch(CountDownLatch countDownLatch){
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                //等待计数器变成0,它会一直阻塞,直到计数器为0
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("wait for other thread finish.");
        }
    }


    public static class MyThread extends Thread {

        CountDownLatch countDownLatch;

        public MyThread(CountDownLatch countDownLatch){
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            int i = 5;
            while (i-- > 0) {
                System.out.println("in thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            //将计数器减1
            countDownLatch.countDown();
        }
    }

执行结果如下:

in thread Thread-0
in thread Thread-1
in thread Thread-2
in thread Thread-0
in thread Thread-1
in thread Thread-2
in thread Thread-0
in thread Thread-1
in thread Thread-2
in thread Thread-0
in thread Thread-1
in thread Thread-2
in thread Thread-0
in thread Thread-1
in thread Thread-2
wait for other thread finish.
Process finished with exit code 0

CyclicBarrier

它是一个可重用的计数器屏障。

它有两个构造函数:

构造函数一

它表示parties条线程都到达某个状态时,就执行barrierAction线程的内容.

    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

构造函数二

它表示parties条线程都到达某个状态时,什么也不执行(可以看到,调用第一个构造函数,并且第二人Runnable的参数为null)

    public CyclicBarrier(int parties) {
        this(parties, null);
    }

例子

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        final int COUNT = 3;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(COUNT, new Runnable() {
            @Override
            public void run() {
                System.out.println("all done.");
            }
        });

        for (int i = 0; i < COUNT; i++) {
            new MyThread(cyclicBarrier).start();
        }
    }


    public static class MyThread extends Thread {

        CyclicBarrier cyclicBarrier;

        public MyThread(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("in thread " + Thread.currentThread().getName());
            try {
                int j = new Random().nextInt(10);
                System.out.println("thread " + Thread.currentThread().getName() + " sleep " + j + " s");
                Thread.sleep(j * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            //通知 CyclicBarrier 该线程已经完成(或者说到了状态),等待其他线程到达了这个状态,再继续执行.
            System.out.println("thread " + Thread.currentThread().getName() + " done.");
            try {
                //这个表示无限等待,等其他所有线程都完毕后,才能继续执行下面的.
                cyclicBarrier.await();
                //,还有个重载方法:它表示通知 CyclicBarrier,它已经到达了某状态,再等1分钟,然后就继续执行下面的方法体了.
                //cyclicBarrier.await(1, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

执行的结果如下:

in thread Thread-0
in thread Thread-1
thread Thread-0 sleep 8 s
thread Thread-1 sleep 4 s
in thread Thread-2
thread Thread-2 sleep 9 s
thread Thread-1 done.
thread Thread-0 done.
thread Thread-2 done.
all done.

Process finished with exit code 0

可用重例子

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        final int COUNT = 3;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(COUNT, new Runnable() {
            @Override
            public void run() {
                System.out.println("all done.");
            }
        });

        for (int i = 0; i < COUNT; i++) {
            new MyThread(cyclicBarrier).start();
        }

        //让上面的先执行,然后再重用 cyclicBarrier
        Thread.sleep(1*1000);

        for (int i = 0; i < COUNT; i++) {
            new MyThread(cyclicBarrier).start();
        }
    }


    public static class MyThread extends Thread {

        CyclicBarrier cyclicBarrier;

        public MyThread(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("in thread " + Thread.currentThread().getName());
            try {
                int j = new Random().nextInt(10);
                System.out.println("thread " + Thread.currentThread().getName() + " sleep " + j + " s");
                Thread.sleep(j * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            //通知 CyclicBarrier 该线程已经完成(或者说到了状态),等待其他线程到达了这个状态,再继续执行.
            System.out.println("thread " + Thread.currentThread().getName() + " done.");
            try {
                //这个表示无限等待,等其他所有线程都完毕后,才能继续执行下面的.
                cyclicBarrier.await();
                //,还有个重载方法:它表示通知 CyclicBarrier,它已经到达了某状态,再等1分钟,然后就继续执行下面的方法体了.
                //cyclicBarrier.await(1, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

执行结果如下:

in thread Thread-0
in thread Thread-1
in thread Thread-2
thread Thread-0 sleep 4 s
thread Thread-1 sleep 0 s
thread Thread-2 sleep 6 s
thread Thread-1 done.
in thread Thread-3
thread Thread-3 sleep 3 s
in thread Thread-4
thread Thread-4 sleep 0 s
thread Thread-4 done.
in thread Thread-5
thread Thread-5 sleep 6 s
thread Thread-0 done.
all done.
thread Thread-3 done.
thread Thread-2 done.
thread Thread-5 done.
all done.

Process finished with exit code 0

可以看到,CountDownLatch只保证(执行构造函数里的Runnable线程之前,肯定是有3条线程执行完毕的)。如此循环.

Semaphore

即信号量,通过 acquire() 方法获得使用资源的权限,通过 release() 方法释放资源占用.

例如,网吧只有5台电脑,但有10个人要上网。这样子,我们就可以用Semaphore来模拟这种情况,代码如下:

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        //只有 5台电脑
        final int COUNT = 5;
        final int PERSON = 10;
        final Semaphore semaphore = new Semaphore(COUNT, true);

        for (int i = 0; i < PERSON; i++) {
            new MyThread(semaphore).start();
        }

    }


    public static class MyThread extends Thread {

        Semaphore semaphore;

        public MyThread(final Semaphore semaphore) {
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                int time = new Random().nextInt(10);
                System.out.println("小混混 " + Thread.currentThread().getName() + " 要使用电脑" + time + "小时 正在使用电脑");
                Thread.sleep(time * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
                System.out.println("小混混 " + Thread.currentThread().getName() + " 用完了电脑");
            }
        }
    }

执行结果如下:

小混混 Thread-0 要使用电脑1小时 正在使用电脑
小混混 Thread-1 要使用电脑9小时 正在使用电脑
小混混 Thread-2 要使用电脑2小时 正在使用电脑
小混混 Thread-3 要使用电脑6小时 正在使用电脑
小混混 Thread-4 要使用电脑9小时 正在使用电脑
小混混 Thread-0 用完了电脑
小混混 Thread-5 要使用电脑2小时 正在使用电脑
小混混 Thread-2 用完了电脑
小混混 Thread-6 要使用电脑8小时 正在使用电脑
小混混 Thread-5 用完了电脑
小混混 Thread-7 要使用电脑8小时 正在使用电脑
小混混 Thread-3 用完了电脑
小混混 Thread-8 要使用电脑9小时 正在使用电脑
小混混 Thread-1 用完了电脑
小混混 Thread-9 要使用电脑5小时 正在使用电脑
小混混 Thread-4 用完了电脑
小混混 Thread-6 用完了电脑
小混混 Thread-7 用完了电脑
小混混 Thread-9 用完了电脑
小混混 Thread-8 用完了电脑

Process finished with exit code 0