学习java线程池

边记录变更新

本文准备开启对java线程池类 ThreadPoolExecutor 的学习,线程池在java线程类库里面是很核心的内容,高级进阶属于必看的内容,简要看看ThreadPoolExecutor 继承实现关系:

ThreadPoolExecutor 类位于 java.util.concurrent之下,ThreadPoolExecutor 继承于 抽象类AbstractExecutorService,AbstractExecutorService抽象又实现了接口类ExecutorService中的方法,接口类ExecutorService继承了接口类Executor。

Executor

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

这个类没有什么特别的,从描述上看execute这个方法,在将来的某个时候执行给定的命令。该命令可以在新线程、池化线程或调用线程中执行,由Executor这个函数实现决定。 并且在这个任务不被接受的时候需要抛出RejectedExecutionException 异常,在执行的任务为空(command参数内容)的时候,要抛出NullPointerException 这个异常。

简单的说,这个接口的任务就是执行某个特定的任务,至于怎么执行这个任务,是创建新线程还是怎么从线程池中获的线程来执行是由实现他的那个类来决定。 并且需要在特定的情况下抛出RejectedExecutionException 异常和 NullPointerException异常。

ExecutorService

ExecutorService是一个接口类,他继承了Executor,并且增加了12个新的方法。抽象类AbstractExecutorService最终去实现了ExecutorService的部分接口,同时ThreadPoolExecutor继承抽象类并实现了未实现的接口

void shutdown();

List<Runnable> shutdownNow();

boolean isShutdown();

boolean isTerminated();

boolean awaitTermination(long timeout, TimeUnit unit)throws InterruptedException;

<T> Future<T> submit(Callable<T> task);

<T> Future<T> submit(Runnable task, T result);

Future<?> submit(Runnable task);

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)throws InterruptedException;

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException;

<T> T invokeAny(Collection<? extends Callable<T>> tasks)throws InterruptedException, ExecutionException;

<T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit)throws InterruptedException, ExecutionException, TimeoutException;

void shutdown()

方法描述的原话,也表示作者想要这个接口实现的内容

    /**
     * Initiates an orderly shutdown in which previously submitted
     * tasks are executed, but no new tasks will be accepted.
     * Invocation has no additional effect if already shut down.
     *
     * <p>This method does not wait for previously submitted tasks to
     * complete execution.  Use {@link #awaitTermination awaitTermination}
     * to do that.
     *
     * @throws SecurityException if a security manager exists and
     *         shutting down this ExecutorService may manipulate
     *         threads that the caller is not permitted to modify
     *         because it does not hold {@link
     *         java.lang.RuntimePermission}{@code ("modifyThread")},
     *         or the security manager's {@code checkAccess} method
     *         denies access.
     */
   void shutdown();
  1. 已经提交的任务(包括正在执行的和队列中等待的),会继续执行完成,停止接收新的submit的任务,如果线程已经中断,调用此方法不会产生额外的效果。
  2. 调用awaitTermination 设置等待的超时时间,达到超时间将后将会再检查一下线程任务是否都完成了,如果全部执行完毕返回true,否则返回false。

List shutdownNow()

    /**
     * Attempts to stop all actively executing tasks, halts the
     * processing of waiting tasks, and returns a list of the tasks
     * that were awaiting execution.
     *
     * <p>This method does not wait for actively executing tasks to
     * terminate.  Use {@link #awaitTermination awaitTermination} to
     * do that.
     *
     * <p>There are no guarantees beyond best-effort attempts to stop
     * processing actively executing tasks.  For example, typical
     * implementations will cancel via {@link Thread#interrupt}, so any
     * task that fails to respond to interrupts may never terminate.
     *
     * @return list of tasks that never commenced execution
     * @throws SecurityException if a security manager exists and
     *         shutting down this ExecutorService may manipulate
     *         threads that the caller is not permitted to modify
     *         because it does not hold {@link
     *         java.lang.RuntimePermission}{@code ("modifyThread")},
     *         or the security manager's {@code checkAccess} method
     *         denies access.
     */
    List<Runnable> shutdownNow();
  1. 尝试将正在执行的任务interrupt中断,忽略正在等待的任务,返回未执行的任务列表。
  2. 调用awaitTermination 设置等待的超时时间,达到超时间将后将会再检查一下线程任务是否都完成了,如果全部执行完毕返回true,否则返回false。这里仍然会存在线程可能无法被中断的情况,需要进一步对这个线程进行逻辑处理。

awaitTermination(long timeOut, TimeUnit unit)

    /**
     * Returns {@code true} if all tasks have completed following shut down.
     * Note that {@code isTerminated} is never {@code true} unless
     * either {@code shutdown} or {@code shutdownNow} was called first.
     *
     * @return {@code true} if all tasks have completed following shut down
     */
    boolean isTerminated();
  1. 阻塞单位时间,等待正在的处理的任务结束,其中timeOut是时间长度,TimeUnit是可选时间单位。如果在规定时间内所有的任务结束,则返回true,如果还有线程仍然在执行,则返回false

代码示例

    public static void main(String[] args) {
        // 创建拥有5个线程的线程池
        ExecutorService pool = Executors.newFixedThreadPool(5);

        // 一个需要执行1秒钟的任务
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("start r1 job..");
                    Thread.sleep(1*1000);
                }catch (InterruptedException e){
                    System.out.println("r1 interrupted:"+e.getMessage());
                }
            }
        };

        // 一个需要执行10秒钟的任务
        Runnable r2 = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("start r2 job..");
                    Thread.sleep(10 * 1000);
                }catch (InterruptedException e){
                    System.out.println("r2 interrupted:"+e.getMessage());
                }
            }
        };

        // 一个执行停不下来的死循环
        Runnable r3 = new Runnable() {
            @Override
            public void run() {
                while (true){ }
            }
        };
//        // 调用正常结束任务
//        pool.execute(r1);
//        // 调用超时中断任务
//        pool.execute(r2);
        // 调用不可中断线程任务
        pool.execute(r3);

        try {
            // 告诉线程池,要关闭了,不能接收新消息,此时线程池不再接收新的任务。
            pool.shutdown();
            // shutdown的时候,可能仍然有正在执行或者准备执行的任务,设置一个等待的超时时间
            if(!pool.awaitTermination(5, TimeUnit.SECONDS)){
                // 我们阻塞了5秒的时间,使得线程池有空间去处理这些任务
                // 如果5秒后,awaitTermination返回false,说明仍然有任务未执行完毕。
                // 这时候我们需要继续调用shutdownNow向超时线程发出中断
                pool.shutdownNow();
                if(!pool.awaitTermination(2,TimeUnit.SECONDS)){
                    // 调用shutdownNow之后,我们阻塞了2秒钟然后等待线程中断的结果。
                    // 如果此时仍然得到false,说明shutdownNow也关闭不了这个线程,需要在此进行对这个或几个特殊线程进行处理
                    System.out.println("我强制关闭了,但是仍然有线程无法关闭");
                }
            }else{
                System.out.println("我一次性完成了");
            }
        }catch (InterruptedException e){
            System.out.println("awaitTermination interrupted: " + e);
            // 抛出了线程被中断的异常,此时需要检查一下线程池是否已经关闭了。若还未关闭,则继续强制中断执行中的线程,
            System.out.println("有一个线程被中断了");
            pool.shutdownNow();
        }
    }

本次代码演示了使用线程池的几种情况:

  1. 执行了shutdown,设置了超时时间,最后任务全部结束,达到我们的预期效果。
  2. 执行shutdown,设置了超时时间,最后仍有线程未执行完毕,执行中断线程进行关闭。
  3. 执行了shutdown,设置了超时时间,最后仍有线程未执行完毕,执行中断处理,仍然无法关闭线程。

关于第3点的解决办法,这里查询了一下官方,并没有怎么说具体怎么解决,但是这应该是一个不好的写法。

官方文档地址

What if a thread doesn't respond to Thread.interrupt?
In some cases, you can use application specific tricks. For example, if a thread is waiting on a known socket, you can close the socket to cause the thread to return immediately. Unfortunately, there really isn't any technique that works in general. It should be noted that in all situations where a waiting thread doesn't respond to Thread.interrupt, it wouldn't respond to Thread.stop either. Such cases include deliberate denial-of-service attacks, and I/O operations for which thread.stop and thread.interrupt do not work properly.

同时我查了其他的资料发现:

shutdownNow方法的作用是向所有执行中的线程发出interrupted以中止线程的运行。这时,各个线程会抛InterruptedException异常(前提是线程中运行了sleep等会抛出异常的方法)。
所以如果你不得不使用r3的任务方法,那么可以这样修改

        Runnable r3 = new Runnable() {
            @Override
            public void run() {
                try {
                    while (true){
                        Thread.sleep(1);
                    }
                }catch (InterruptedException e){
                    System.out.println("r3 interrupted:"+e.getMessage());
                }
            }
        };

在线程循环的时候,每次都停止1毫秒, 这个sleep的作用是为了让后面的interrupted可以终止这个死循环线程。当然这个是不得已而为之,在写法上也不推荐while(true),最好是通过判断条件结果来决定是否继续运行下去。例如

        Runnable r3 = new Runnable() {
            @Override
            public void run() {
                int i = 0;
                while (i<10){
                	i++;
                }
            }
        };

总之在使用线程执行任务的时候,需要谨慎的处理。线程死循环,最大的坏处,就是导致cpu使用率增加,如果跟其他线程有竞争资源现象的话,造成死锁可能性大。也可能会导致jvm堆内存不断攀升,full gc频率会加大,最终如果堆溢出,可能会出现线程退出,或者OOM。

未完待续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值