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

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);
    }
  • 8
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 如果需要关闭某个线程,可以通过断(interrupt)线程的方式终止它的执行。示例代码如下: ```java ExecutorService executor = Executors.newFixedThreadPool(10); Future<?> future = executor.submit(() -> { // 异步任务的代码 }); // 断正在执行的任务 future.cancel(true); ``` 在调用submit()方法后,会返回一个Future对象,表示异步任务的执行结果。可以通过调用该对象的cancel()方法来断任务的执行。如果传入的参数为true,则会尝试断任务的执行,否则会等待任务执行完成。 需要注意的是,断任务的执行并不会立即生效,而是需要在异步任务自行判断线程状态,然后在合适的时机终止任务的执行。示例代码如下: ```java ExecutorService executor = Executors.newFixedThreadPool(10); Future<?> future = executor.submit(() -> { while (!Thread.currentThread().isInterrupted()) { // 异步任务的代码 } }); // 断正在执行的任务 future.cancel(true); ``` 在异步任务,可以通过判断Thread.currentThread().isInterrupted()的返回值来判断线程是否断。如果返回true,则表示线程断,需要终止任务的执行。在任务的代码,可以通过抛出InterruptedException异常来终止任务的执行,示例代码如下: ```java ExecutorService executor = Executors.newFixedThreadPool(10); Future<?> future = executor.submit(() -> { try { while (!Thread.currentThread().isInterrupted()) { // 异步任务的代码 } } catch (InterruptedException e) { // 终止任务的执行 } }); // 断正在执行的任务 future.cancel(true); ``` ### 回答2: 在使用Java异步服务时,线程池的正确关闭是非常重要的。如果线程池不能正常关闭,将导致线程暴增,从而导致系统资源耗尽,甚至宕机。以下是一个示例代码,展示了如何正确关闭线程池。 ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class AsynchronousServiceExample { private ExecutorService threadPool; public void startService() { // 初始化线程池 threadPool = Executors.newFixedThreadPool(10); // 启动异步任务 for (int i = 0; i < 100; i++) { threadPool.submit(() -> { // 异步任务的具体逻辑 // ... }); } } public void stopService() { // 关闭线程池并等待任务执行完成 threadPool.shutdown(); // 请求线程池关闭 try { if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) { // 等待线程池的任务执行完毕,最多等待60秒 threadPool.shutdownNow(); // 强制关闭线程池 if (!threadPool.awaitTermination(60, TimeUnit.SECONDS)) { // 如果线程池仍未关闭,则输出错误日志 System.err.println("线程池未能正常关闭"); } } } catch (InterruptedException e) { // 捕获线程断异常 threadPool.shutdownNow(); // 如果线程等待时被断,则强制关闭线程池 Thread.currentThread().interrupt();// 重新设置线程状态 } } } ``` 在上述示例代码,我们首先利用`Executors.newFixedThreadPool()`方法创建线程池,大小为10,并启动了100个异步任务。当需要关闭线程池时,调用`threadPool.shutdown()`方法来请求线程池关闭。然后,使用`threadPool.awaitTermination()`方法等待线程池的任务执行完成,最多等待60秒。如果超过60秒仍有任务没有执行完毕,使用`threadPool.shutdownNow()`方法强制关闭线程池。最后,通过判断线程池是否成功关闭来决定是否输出错误日志。为了处理线程断异常,我们在`awaitTermination()`方法和`shutdownNow()`方法之后重新设置了线程状态,以确保程序的稳定性。 ### 回答3: 当使用线程池时,可能会遇到线程暴增的问题。为了解决这个问题,可以采取以下步骤来关闭线程: 步骤1: 停止向线程池提交新的任务。这可以通过调用线程池的shutdown方法来完成,该方法将不再接受新任务,并尝试将所有已提交但未执行的任务完成。 步骤2: 等待线程池的任务执行完成。可以使用awaitTermination方法来等待,该方法将会等待指定时间,直到所有任务都执行完成或者超时。 步骤3: 如果在等待时间内,线程池的任务仍未执行完成,可以使用shutdownNow方法来止所有未执行的任务。该方法将会发送断信号给线程池线程,使其尽快停止执行。 下面是一个示例代码,演示了如何使用线程池执行异步任务,并关闭线程池: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ThreadPoolExample { public static void main(String[] args) { // 创建线程池,指定最大线程数 ExecutorService executor = Executors.newFixedThreadPool(10); // 提交任务给线程池执行 for (int i = 0; i < 100; i++) { executor.execute(new Worker()); } // 停止向线程池提交新的任务 executor.shutdown(); try { // 等待线程池的任务执行完成,最多等待1小时 if (!executor.awaitTermination(1, TimeUnit.HOURS)) { // 在等待时间内,任务未执行完成,进行断处理 executor.shutdownNow(); // 等待线程池的任务止 if (!executor.awaitTermination(1, TimeUnit.MINUTES)) { System.err.println("线程池未能完全停止"); } } } catch (InterruptedException e) { // 发生断异常,进行断处理 executor.shutdownNow(); } } private static class Worker implements Runnable { @Override public void run() { // 执行具体的任务逻辑 // ... } } } ``` 上述示例代码,首先创建了一个固定大小的线程池,然后使用execute方法提交任务给线程池执行。接着调用shutdown方法停止向线程池提交新的任务,然后使用awaitTermination方法等待线程池的任务执行完成。如果等待时间超过指定的超时时间(在示例为1小时),则调用shutdownNow方法止未执行的任务。最后,再次等待线程池的任务止,如果超时,则打印一条警告信息。如果在任何等待期间发生了InterruptedException异常,则会调用shutdownNow方法止未执行的任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值