JUC-1.2-线程池-钩子方法的使用

上文提到了线程池的钩子方法,其实就是线程池在执行每个任务的前后执行一些操作,还有线程池的暂停与继续,等等一些辅助功能,下面看一下如何使用

钩子方法的使用

ThreadPoolExecutor 提供了3个钩子方法,需要子类去根据自己的需要重写,三个方法如下:

protected void beforeExecute(Thread t, Runnable r) { } // 任务执行前
protected void afterExecute(Runnable r, Throwable t) { } // 任务执行后
protected void terminated() { } // 线程池执行结束后

所以我们要新建一个类,然后继承 ThreadPoolExecutor ,重写这几个方法

@Slf4j
public class PauseableThreadPool extends ThreadPoolExecutor {

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }

    private final ReentrantLock lock = new ReentrantLock();

    private Condition unPaused = lock.newCondition();

    private boolean isPaused;

    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        lock.lock();
        try {
            while (isPaused) {
                // 如果是暂停状态 阻塞挂起
                unPaused.await();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        // 每个任务执行完成之后,输出
        log.info("执行完了一个任务...");
    }

    @Override
    protected void terminated() {
        super.terminated();
        log.info("线程池已经关闭....");
    }

    /**
     * 暂停线程池
     */
    public void pause(){
        lock.lock();
        try {
            // 设置状态为暂停
            isPaused = true;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 继续执行
     */
    public void resume(){
        lock.lock();
        try {
            isPaused = false;
            // 唤醒
            unPaused.signalAll();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        PauseableThreadPool threadPool = new PauseableThreadPool(5, 5, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

        Runnable task = ()->{
            log.info("执行任务....");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };

        for (int i = 0; i < 1000; i++) {
            threadPool.execute(task);
        }

        Thread.sleep(1000);
        threadPool.pause();
        log.info("线程池已暂停");

        Thread.sleep(10000);
        threadPool.resume();
        log.info("线程池恢复执行");

    }

}

代码看着比较多,但是很简单, 上面是一堆构造不用管,然后就是个暂停和继续方法, 其实就是操作一个共享变量,然后再每个任务运行之前会调用 beforeExecute() 方法,这个方法会判断暂停状态是否为true, 是的话就阻塞,之后调用了继续的方法后再唤醒继续执行

然后写个main方法去测试一下效果

public static void main(String[] args) throws InterruptedException {
    PauseableThreadPool threadPool = new PauseableThreadPool(5, 5, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());

    Runnable task = ()->{
        log.info("执行任务....");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };

    for (int i = 0; i < 1000; i++) {
        threadPool.execute(task);
    }

    Thread.sleep(1000);
    threadPool.pause();
    log.info("线程池已暂停");

    Thread.sleep(10000);
    threadPool.resume();
    log.info("线程池恢复执行");

    threadPool.shutdown();
}

总结

掌握钩子方法的作用,以及如何实现自定义的钩子方法

发布了76 篇原创文章 · 获赞 1 · 访问量 5075

猜你喜欢

转载自blog.csdn.net/qq_38083545/article/details/105602242