• java并发编程(五)——线程池


    线程池

    什么是线程池

    我们可以在不影响一个线程的情况下创建另一个线程去完成任务,这样可以提高执行效率。但是如果同一时刻有大量的任务需要执行,而这些任务又比较简单,那么每次都创建一个新的线程肯定会耗费很多时间。为了减少创建线程所花费的时间,人们想出了线程池的办法。线程池中预先创建了一定数量的线程,当需要线程执行任务时就从线程池中取,当任务执行完后,线程不会消亡,而是继续执行其它任务。

    线程池的优点:

    1、减少资源开销。不会每次都创建新线程,而是从线程池中取。

    2、提高响应速度。线程已经提前创建好了,需要执行任务时直接用就可以了。

    3、便于管理。线程是一种稀缺资源,如果不加以控制,不仅会浪费大量资源,而且可能影响系统的稳定性。使用线程池可以对线程的创建和停止,线程的数量加以控制,使线程处在可控的范围内,不仅便于管理,而且方便调优。

    线程池的构成:一般来说线程池由下面几部分构成

    1、核心线程。已经创建好的并处在运行状态的一定数量的线程,它们不断从阻塞队列中获取任务并执行。

    2、阻塞队列。用来存储工作线程来不及处理的任务,当所有的工作线程都被占用后,之后的任务就会进入阻塞队列,等待某个线程执行完后才有机会被处理。

    线程池的创建:创建线程池有两种方式

    1、使用 ThreadPoolExecutor 类

    2、使用 Executors 类

    ThreadPoolExecutor

    Excutor接口

    ThreadPoolExecutor的顶层接口是 Excutor,Excutor接口位于 java.util.concurrent 包下,该接口中只提供了一个用于接收Runnable类型参数的方法excute(Runnabel task),用于接收一个任务。我们之前使用Thread类来创建线程并执行任务。但是在Excutor中我们可以这样创建一个线程并执行任务。

    public class Demo implements Executor{
    
        @Override
        public void execute(Runnable r) {
            r.run();   //既可以在execute方法中直接执行任务
            new Thread(r).start();   //也可以新建一个线程去执行任务
        }
    }

    ExcutorService接口

    ExcutorService 是一个比 Excutor 使用更广泛的子接口,提供了线程生命周期管理的方法。Excutor 中只有一个用于接收任务的excute方法,该方法没有返回值。而 ExcutorService 的submit()方法可以返回一个Future对象,用来获取任务的执行结果。ExcutorService 拓展了 Excutor,在开发中使用的更多。下面是两者的区别:

    ExecutorExecutorService
    Executor 是 Java 线程池的核心接口,用来并发执行提交的任务 ExecutorService 是 Executor 接口的扩展,提供了异步执行和关闭线程池的方法
    提供execute()方法用来提交任务 提供submit()方法用来提交任务
    execute()方法无返回值 submit()方法返回Future对象,可用来获取任务执行结果
    不能取消任务 可以通过Future.cancel()取消pending中的任务
    没有提供和关闭线程池有关的方法 提供了关闭线程池的方法

    ThreadPoolExecutor

    从上面的类图我们看到 ExcutorService 接口的实现类是 AbstractExecutorService,这是一个抽象类,而这个类的子类就是ThreadPoolExecutor。

    创建线程池

    我们可以通过ThreadPoolExecutor类的构造方法来创建线程池。这个类共有四个构造方法

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler) {
            if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
                throw new IllegalArgumentException();
            if (workQueue == null || threadFactory == null || handler == null)
                throw new NullPointerException();
            this.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
        }

    上面就是 ThreadPoolExecutor 类中的一个构造方法。该方法一共有七个参数:

    • corePoolSize:线程池中核心线程的数目。当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程。
    • maximumPoolSize:线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的救急线程执行任务。值得注意的是,如果使用了无界的任务队列这个参数就没什么效果。
    • keepAliveTime:救急线程空闲后存活的时间。所以,如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。
    • unit:救急线程存活的时间单位。
    • workQueue:任务队列。用来保存等待执行的任务的阻塞队列。
    • threadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
    • handler:饱和策略。当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。

    提交任务

    通过构造方法创建了线程池之后,可以使用execute()和submit()向线程池中提交任务,这两个方法都是接收 Runnabel 类型的参数,当然,submit() 比 execute() 功能更多。

    execute方法用于不需要返回值的任务,所以无法判断任务是否被线程池执行成功。

    threadsPool.execute(new Runnable() {
        @Override
        public void run() {
            // TODO Auto-generated method stub
        }
    });

    submit方法会返回一个Future对象。通过这个对象可以判断任务是否执行成功,并且通过Future的get()可以获取返回值。下面是get方法的实现

    public V get() throws InterruptedException, ExecutionException {
            int s = state;
            // 只要状态值小于 COMPLETING, 就说明任务还未完成, 去等待完成
            if (s <= COMPLETING)
                s = awaitDone(false, 0L);
            // 只要等待完成, 再去把结果取回即可
            return report(s);
        }

    下面代码是使用submit执行任务,并获取任务的返回值

    Future<Object> future = executor.submit(harReturnValuetask);
    try {
        Object s = future.get();
    } catch (InterruptedException e) {
        // 处理中断异常
    } catch (ExecutionException e) {
        // 处理无法执行任务异常
    } finally {
        // 关闭线程池
        executor.shutdown();
    }

    关闭线程池

    可以通过线程池的 shutdown() 和 shutdownNow() 来关闭线程池。

    shutdown():将线程池的状态设置为SHUTDOWN,然后中断所有没有执行任务的线程。

    shutdownNow():将线程池的状态设置为STOP,然后中断所有线程并返回等待执行的任务列表。

    线程池的状态

     线程池使用int的高三位来存储线程池的状态,其它的位数用来存储线程的数量,这些信息用是原子变量 ctl 来进行存储的。之所以这么做是为了只进行一次CAS操作就可以既改变线程池的状态,又可以改变线程池中线程的数量。线程池共有五种状态:

    1、RUNNING(运行状态):线程池被创建后就处于RUNNIG状态,接受新任务,并且也能处理阻塞队列中的任务。

    2、SHUTDOWN(关闭状态):不接受新任务,但可以处理阻塞队列中的任务。

    3、STOP(停止状态):中断正在执行的任务,并且抛弃阻塞队列的任务。

    4、TIDYIDNG(整理状态):如果所有的任务都执行完了,线程池就会进入该状态。该状态会执行 terminated 方法进入TERMINATED。

    5、TERMINATED(终止状态):执行完 terminated 方法后就会进入该状态,默认 terminated 方法什么也不做。

    Excutors

    通过 ThreadPoolExcutor 可以创建一个线程池,但是上面的方法有些复杂,方法的参数过多,给开发造成不必要的麻烦。所以在JDK1.5中就引入了一个工具类 Excutors,这个类提供了众多的工厂方法,可以更方便的方式创建线程池。下面是通过Excutors来创建线程池。

     通过Executors可以创建四种线程池

    newFixedThreadPool

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>(),
                                          threadFactory);
        }

    参考资料

    深入浅出Java线程池

    Java中的线程池

    java线程池详解

    Java中线程池ThreadPoolExecutor原理探究

    java线程池之一:创建线程池的方法

  • 相关阅读:
    LOJ#2245 魔法森林
    洛谷P1173 [NOI2016]网格
    [NOI2018]归程
    宇宙旅行
    hdu 4027 Can you answer these queries?(线段树)
    poj 1661 Help Jimmy(记忆化搜索)
    hdu 1078 FatMouse and Cheese(简单记忆化搜索)
    poj 3616 Milking Time (基础dp)
    hdu 1074 Doing Homework(状压dp)
    codeforces 735C. Tennis Championship(贪心)
  • 原文地址:https://www.cnblogs.com/Zz-feng/p/13213610.html
Copyright © 2020-2023  润新知