判断线程池中某个线程是否执行完成

  • Post author:
  • Post category:其他




1.先写结果

使用FutureTask类即可实现判断线程池中的线程的状态,提供的方法是isDone(),get()。

    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;



2.判断某个线程是否执行完成(不使用线程池)

在不适用线程池的情况下,一般使用Thread类提供的isAlive方法来判断线程的状态。当线程开始处于活动状态时,返回true,当线程未开始或者已结束等其他状态,返回false。

    public static void main(String[] args) {
        Thread thread = new Thread(() -> System.out.println("线程开始执行"));
        thread.start();
        System.out.println(thread.isAlive());
        //使用while循环判断线程是否执行完成
    }



3.在线程池中不能使用isAlive判断线程状态的原因



3-1.错误示例

    public static void main(String[] args) {
        Thread task = new Thread(() -> {
            System.out.println("线程1开始");
            try {
                Thread.sleep(10000); //10s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread task2 = new Thread(() -> {
            System.out.println("线程2开始");
            try {
                Thread.sleep(20000); //20s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        fixedThreadPool.execute(task);
        fixedThreadPool.execute(task2);
        fixedThreadPool.shutdown();
		
		//判断线程池是否执行完成
        while (!fixedThreadPool.isTerminated()) {
            System.out.println("还在执行...");
            if (task.isAlive()) { //理论上前10s这里应该是true,但是实际返回的是false
                System.out.println("线程1执行完成");
            }
            if (task2.isAlive()) { //理论上前20s这里应该是true,但是实际返回的是false
                System.out.println("线程2执行完成");
            }
            try {
                Thread.sleep(2000); //2s
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("主线程结束");
    }


原因:isAlive方法是针对于类来说的,当前Thread类是否执行过start方法就决定了isAlive方法返回的是否是true。而在线程池中,真正创建线程并运行的是ThreadFactory工厂类。



3-2.创建线程工厂

下面是创建Thread的源码,可以看到只是调用我们传入Runnable的run方法而已。这个返回的才是真正的在线程池中运行的线程。

public Thread newThread(final Runnable r) {
            if (group.isDestroyed()) {
                group = new ThreadGroup(group.getParent(), name + "-workqueue");
            }
            Runnable wrapped = new Runnable() {
                public void run() {
                    ++approxThreadCount;
                    try {
                        r.run();
                    } finally {
                        --approxThreadCount;
                    }
                }
            };
            final Thread t = new Thread(group, 
                                  wrapped, 
                                  name + "-workqueue-" + threadNumber.getAndIncrement(),
                                  0);
            AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                public Boolean run() {
                    t.setContextClassLoader(loader);
                    return true;
                }
            });
            t.setDaemon(true);
            if (t.getPriority() != Thread.NORM_PRIORITY) {
                t.setPriority(Thread.NORM_PRIORITY);
            }
            return t;
        }



3-3.创建线程方法(ThreadPoolExecutor)

ThreadPoolExecutor中创建线程(创建Worker时)。

private boolean addWorker(Runnable firstTask, boolean core) {
//firstTask就是我们一开始传入的类
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask); //调用了上面的创建工厂方法
            final Thread t = w.thread; //得到了一个新的Thread
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start(); //启动了Thread,所以说我们传入的Thread压根没有执行,只是执行了我们的run方法
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }



4.解决问题,实现判断线程池中的线程是否执行完成



4-1.FutureTask

使用idea的查看类继承结构(ctrl+alt+shift+U):

继承结构



4-2.实现代码(使用isDone)

public static void main(String[] args) {

        FutureTask<Boolean> futureTask = new FutureTask<>(() -> {
            System.out.println("线程1开始");
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":线程1结束");
            return true;
        });
        FutureTask<Boolean> futureTask1 = new FutureTask<>(() -> {
            System.out.println("线程2开始");
            try {
                Thread.sleep(20 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":线程2结束");
            return true;
        });

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        fixedThreadPool.execute(futureTask);
        fixedThreadPool.execute(futureTask1);
        fixedThreadPool.shutdown();

        while (!fixedThreadPool.isTerminated()) {
            System.out.println("还在执行...");
            System.out.println("1:"+futureTask.isDone()); //前10s输出false,后10s输出true
            System.out.println("2:"+futureTask1.isDone()); //20s输出false
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("1:"+futureTask.isDone()); //true
        System.out.println("2:"+futureTask1.isDone()); //true
        System.out.println("主线程结束");
    }

使用isDone()来判断线程是否结束,不是NEW状态就返回true,包括线程已经取消或者异常

    public boolean isDone() {
        return state != NEW;
    }



4-3.实现代码(使用get)

还可以通过get()方法来获取返回值,因为FutureTask是有返回值的,在线程执行完成之前,get()方法会一直处于堵塞状态。这样就可以在某个线程执行完成后再执行其他相关的代码。

    public static void main(String[] args) {

        FutureTask<Boolean> futureTask = new FutureTask<>(() -> {
            System.out.println("线程1开始");
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":线程1结束");
            return true;
        });
        FutureTask<Boolean> futureTask1 = new FutureTask<>(() -> {
            System.out.println("线程2开始");
            try {
                Thread.sleep(20 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":线程2结束");
            return true;
        });

        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        fixedThreadPool.execute(futureTask);
        fixedThreadPool.execute(futureTask1);
        fixedThreadPool.shutdown();

        try {
            Boolean isSuccess = futureTask.get(); //10s后返回结果
            System.out.println("获取到线程1执行结果:" + isSuccess);
            Boolean isSuccess1 = futureTask1.get(); //20s后返回结果
            System.out.println("获取到线程2执行结果:" + isSuccess1);
            if (isSuccess && isSuccess1) {
                System.out.println("线程执行完毕");
            }
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("线程异常");
        }
        System.out.println("主线程结束");
    }

使用get()来获取线程的返回值,当获取到线程的返回值时,说明线程已经执行完毕。

get()方法的重载方法get(long timeout, TimeUnit unit)方法,可以设置最长堵塞时间,以免堵塞时间过长出现其他问题。

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }



版权声明:本文为weixin_44269022原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。