Java等待所有的线程运行结束解决方案总结(8种)

Java架构师交流群:793825326

java版本:jdk1.8

IDE:Idea2019

实际开发中,经常会碰到这种问题,某个线程需要等待所有的线程执行结束了,才可以继续执行。要实现这个效果,Java中有很多方案,下面我列举一些我所知道的方案:

方案一:join。代码如下:

Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread1 start");
                    for (int i=0;i<10;i++)
                    {
                        Thread.sleep(500);
                        System.out.println("thread1 is working");
                    }
                    System.out.println("thread1 end");
                }
                catch (Exception ex) {System.out.println(ex.toString());}
            }
        });

        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread2 start");
                    for (int i=0;i<5;i++)
                    {
                        Thread.sleep(500);
                        System.out.println("thread2 is working");
                    }
                    System.out.println("thread2 end");
                }
                catch (Exception ex) {System.out.println(ex.toString());}
            }
        });

        Thread thread3=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread3 start");

                    System.out.println("thread3 end");
                }
                catch (Exception ex) {System.out.println(ex.toString());}
            }
        });
        try {
            thread1.start();
            thread2.start();
            thread1.join();
            thread2.join();
            thread3.start();

        }
        catch (Exception ex)
        {
            System.out.println(ex.toString());
        }
}

该段代码的运行结果如下:

thread1 start
thread2 start
thread1 is working
thread2 is working
thread2 is working
thread1 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread2 end
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 end
thread3 start
thread3 end

thread1.start();
thread2.start();
thread1.join();
thread3.start();
thread2.join();

join的作用是一个子线程加入到当前线程,并导致当前线程等待,让其中调用了join方法的子线程运行,直到子线程运行结束了才会继续往下执行,join不会阻塞代码往下执行,也就是说不会阻塞其他的线程Join,比如本例中虽然thread1.join();方法被调用了,并不妨碍thread2.join();的执行,即便你把这句代码放到thread3.start();之后:

其运行结果仍然是一样的:

thread2 start
thread1 start
thread2 is working
thread1 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread2 is working
thread1 is working
thread2 is working
thread1 is working
thread2 end
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 end
thread3 start
thread3 end

方案二:wait/notifyAll,代码如下:

private static Object lock=new Object();
private static final AtomicInteger count = new AtomicInteger(2);
public static void main(String[] args) {

    Thread thread1=new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println("thread1 start");
                for (int i = 0; i < 10; i++) {
                    Thread.sleep(500);
                    System.out.println("thread1 is working");
                }
                System.out.println("thread1 end");
                synchronized (lock) {
                    if (count.decrementAndGet() == 0)
                        lock.notifyAll();
                }
            } catch (Exception ex) {
                System.out.println(ex.toString());
            }
        }
    });

    Thread thread2=new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println("thread2 start");
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(500);
                    System.out.println("thread2 is working");
                }
                System.out.println("thread2 end");
                synchronized (lock) {
                    if (count.decrementAndGet() == 0)
                        lock.notifyAll();
                }

            } catch (Exception ex) {
                System.out.println(ex.toString());
            }
        }
    });

    Thread thread3=new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                synchronized (lock) {
                    lock.wait();
                }
                System.out.println("thread3 start");

                System.out.println("thread3 end");
            } catch (Exception ex) {
                System.out.println(ex.toString());
            }
        }
    });
    try {
        thread1.start();
        thread2.start();
        thread3.start();
    }
    catch (Exception ex)
    {
        System.out.println(ex.toString());
    }
}

其执行结果如下:

thread1 start
thread2 start
thread2 is working
thread1 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread2 is working
thread1 is working
thread1 is working
thread2 is working
thread2 end
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 end
thread3 start
thread3 end

这方法其实本质上和加锁的区别就是把耗时的代码不加锁,然后增加一个标记量来判断是否让thread3开始执行罢了。

方法三:CountDownLatch,代码如下:

private static final CountDownLatch latch=new CountDownLatch(2);
    public static void main(String[] args) {

        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread1 start");
                    for (int i = 0; i < 10; i++) {
                        Thread.sleep(500);
                        System.out.println("thread1 is working");
                    }
                    System.out.println("thread1 end");
                    latch.countDown();
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });

        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread2 start");
                    for (int i = 0; i < 5; i++) {
                        Thread.sleep(500);
                        System.out.println("thread2 is working");
                    }
                    System.out.println("thread2 end");
                    latch.countDown();
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });

        Thread thread3=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    latch.await();
                    System.out.println("thread3 start");
                    System.out.println("thread3 end");
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });
        try {
            thread1.start();
            thread2.start();
            thread3.start();
        }
        catch (Exception ex)
        {
            System.out.println(ex.toString());
        }
}

运行结果如下:

thread1 start
thread2 start
thread1 is working
thread1 is working
thread2 is working
thread2 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread2 end
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 end
thread3 start
thread3 end

这个方法和方法二思路一样,增加一个标记量,通过判断标记量的值来确定代码是否继续执行。

方法四:ExecutorService,代码:

private static final ExecutorService EXECUTOR_SERVICE= Executors.newCachedThreadPool();
    public static void main(String[] args) {

        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread1 start");
                    for (int i = 0; i < 10; i++) {
                        Thread.sleep(500);
                        System.out.println("thread1 is working");
                    }
                    System.out.println("thread1 end");
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });

        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread2 start");
                    for (int i = 0; i < 5; i++) {
                        Thread.sleep(500);
                        System.out.println("thread2 is working");
                    }
                    System.out.println("thread2 end");
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });

        Thread thread3=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    if (EXECUTOR_SERVICE.awaitTermination(10,TimeUnit.SECONDS))
                    {
                        System.out.println("thread3 start");
                        System.out.println("thread3 end");
                    }
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });
        try {
            EXECUTOR_SERVICE.execute(thread1);
            EXECUTOR_SERVICE.execute(thread2);
            EXECUTOR_SERVICE.shutdown();
            thread3.start();
        }
        catch (Exception ex)
        {
            System.out.println(ex.toString());
        }
}

执行结果如下:

thread1 start
thread2 start
thread2 is working
thread1 is working
thread1 is working
thread2 is working
thread2 is working
thread1 is working
thread2 is working
thread1 is working
thread1 is working
thread2 is working
thread2 end
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 end
thread3 start
thread3 end

这个方法和之前的区别是它限定了等待时间,其触发逻辑是:当thread1和thread2都执行结束了,则awaitTermination返回true,如果在限定的10s内还没结束,则10s时间到时返回false。

方法五:信号量Semaphore,代码如下:

private static final Semaphore semaphore=new Semaphore(2);
    public static void main(String[] args) {

        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore.acquire();
                    System.out.println("thread1 start");
                    for (int i = 0; i < 10; i++) {
                        Thread.sleep(500);
                        System.out.println("thread1 is working");
                    }
                    System.out.println("thread1 end");
                    semaphore.release();
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });

        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore.acquire();
                    System.out.println("thread2 start");
                    for (int i = 0; i < 5; i++) {
                        Thread.sleep(500);
                        System.out.println("thread2 is working");
                    }
                    System.out.println("thread2 end");
                    semaphore.release();
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });

        Thread thread3=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    semaphore.acquireUninterruptibly(2);
                    System.out.println("thread3 start");
                    System.out.println("thread3 end");
                    semaphore.release();
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });
        try {
            thread1.start();
            thread2.start();
            thread3.start();
        }
        catch (Exception ex) {
            System.out.println(ex.toString());
        }
}

执行结果如下:

thread2 start
thread1 start
thread2 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread2 end
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 end
thread3 start
thread3 end

这个方法的就是定义一个有两个许可证的信号量,thread3尝试去获取两个许可证,一开始它是获取不到的,因为会被thread1和thread2占用,那么它就会一直阻塞,直到这两个线程执行结束,thread3才拿到两个许可证,继续执行后面的代码。

方法六:自定义标记量(自旋锁),代码如下:

private static volatile int count=2;
    public static void main(String[] args) {

        Thread thread1=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread1 start");
                    for (int i = 0; i < 10; i++) {
                        Thread.sleep(500);
                        System.out.println("thread1 is working");
                    }
                    System.out.println("thread1 end");
                    count--;
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });

        Thread thread2=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("thread2 start");
                    for (int i = 0; i < 5; i++) {
                        Thread.sleep(500);
                        System.out.println("thread2 is working");
                    }
                    System.out.println("thread2 end");
                    count--;
                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });

        Thread thread3=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (count>0);
                        System.out.println("thread3 start");
                        System.out.println("thread3 end");

                } catch (Exception ex) {
                    System.out.println(ex.toString());
                }
            }
        });
        try {
            thread1.start();
            thread2.start();
            thread3.start();
        }
        catch (Exception ex) {
            System.out.println(ex.toString());
        }
}

执行结果如下:

thread1 start
thread2 start
thread1 is working
thread2 is working
thread1 is working
thread2 is working
thread2 is working
thread1 is working
thread1 is working
thread2 is working
thread2 is working
thread2 end
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 is working
thread1 end
thread3 start
thread3 end

这个方法实际上和方法三是一个道理,方法三的实现方案,如果你看了源码,你会发现它用了一个无限for循环。这里我们自己定义了一个标记量,如果这个标记量大于0,就一直循环等待,直到满足条件,代码继续往下执行。另外本例中的代码是有瑕疵的,count--;这步操作应该放到finally里面,这样才不会导致出现异常的时候thread3一直等待。

7.CyclicBarrier方法:

CyclicBarrier barrier=new CyclicBarrier(5);
for (int i=0;i<4;i++)
{
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println("线程"+Thread.currentThread().getId()+"开始执行");
                Thread.sleep(2000);
                System.out.println("线程"+Thread.currentThread().getId()+"执行结束");
                barrier.await();
            }catch (Exception ex)
            {
                System.out.println(ex);
            }
        }
    }).start();
}
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            barrier.await();
            System.out.println("等待线程开始执行");
            Thread.sleep(2000);
            System.out.println("等待线程执行结束");
        }catch (Exception ex)
        {
            System.out.println(ex);
        }

    }
}).start();

CyclicBarrier的作用是等待所有的线程(按照初始化的时候给的线程数,这里是5个)都await后,再集中让所有的线程继续。

8.Condition方法,这个和waite/notifyAll是一样的,前者本身就是后者的优化,详细信息可以看我的另一篇博客https://blog.csdn.net/dap769815768/article/details/96720712

在这个问题上,具体的实现思路二者是完全一致的,加一个标记量,当所有的线程都云心结束了,调用Condition的signalAll方法让等待线程执行。具体的代码这里就不贴了。

  • 11
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值