Java--线程池

作者: aruba | 来源:发表于2021-10-15 15:38 被阅读0次
Java中提供了一系列和锁相关的工具:synchronized、ReentrantLock、AtomicXXX类,它们都能帮助解决我们进行多线程操作时并发问题,之前我们也提到了内核线程模型,Hotspot虚拟机线程的创建都是通过内核线程提供的轻量级进程API创建的,反复的创建线程会不断的由OS线程调度,这也会消耗大量CPU性能
争对创建线程,除了使用Thread之外,Java还提供了线程池类,它们最终都实现的Executor接口,JDK1.7中的继承实现关系如下:

一、使用线程池与Thread对比

使用Thread创建10000个线程,并运行,记录时间:

public class ThreadPoolTest {

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        List<Thread> threads = new ArrayList<>(10000);

        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread();
            threads.add(thread);
            thread.start();
        }

        for(Thread t:threads){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("ms:"+(System.currentTimeMillis() - startTime));
    }

}

结果:
ms:1761

使用线程池:

public class ThreadPoolTest2 {

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        int size = 10000;

        ExecutorService executorService = Executors.newSingleThreadExecutor();
        final CountDownLatch countDownLatch = new CountDownLatch(size);

        for (int i = 0; i < size; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    countDownLatch.countDown();
                }
            });
        }

        try {
            countDownLatch.await();//等待10000计数结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdown();
        System.out.println("ms:" + (System.currentTimeMillis() - startTime));
    }

}

结果:
ms:69

两者相差的时间为100倍以上,这边使用的线程池回头再来看,由这个例子可以看出,线程的创建到运行过程,会花费大量的cpu时间,争对需要大量并发的操作,使用线程池可以大大提高程序性能

二、ThreadPoolExecutor

1.ThreadPoolExecutor执行任务图解

Executors是个帮助类,内部实际使用的是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大致的执行场景如下:


execute方法执行流程
2.ThreadPoolExecutor参数

再来看构造函数中各个参数的含义:

  • corePoolSize:核心线程最大数,当有新任务时,如果小于该值,那么会直接创建线程或使用空闲的核心线程执行任务

  • maximumPoolSize:最大线程数,需大于核心线程数,非核心线程最大数 = 最大线程数 - 核心线程最大数,当核心线程全部创建并都在执行任务,那么新任务使用非核心线程执行,同样使用核心线程创建机制:创建线程或使用空闲的线程,非核心线程可以指定死亡时间,相当于临时工

  • keepAliveTime:非核心线程的保持存活时间,当一个非核心线程持续keepAliveTime时间都处于空闲状态,那么使它消亡

  • unit:保持存活时间的时间单位

  • workQueue:任务队列,当线程总数到达最大,并且所有线程都有任务在执行,那么新任务可以进入该队列,等待有线程空闲时执行队列中的任务。通过Executors创建的任务队列,都没有最大值,所以当遇到服务器并发量大的情况,容易OOM,安卓开发中不需要考虑这个问题

  • threadFactory:创建线程工厂,一般使用默认的即可

  • handler:拒绝策略,当触发拒绝时,执行该对象策略策略

3.任务队列

任务队列实现BlockingQueue接口,JDK提供了以下4种:

  • ArrayBlockingQueue: 是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
  • LinkedBlockingQueue: 一个基于链表结构的阻塞队列,此队列按FIFO(先进先出)排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
  • SynchronousQueue: 一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
  • PriorityBlockingQueue: 一个具有优先级的无限阻塞队列
4.拒绝策略
  • DiscardPolicy:丢弃当前将要加入队列的任务本身。
  • DiscardOldestPolicy:丢弃任务队列中最旧任务。(丢弃最旧任务也不是简单的丢弃最旧的任务,而是有一些额外的处理)
  • AbortPolicy:抛出异常的方式。
  • CallerRunsPolicy:不进入线程池执行,由调用者线程执行。

三、Executors

后台一般不允许使用Executors,但我们安卓开发是可以使用的,接下来来使用Executors三种线程池,有了上面ThreadPoolExecutor的了解,理解起来很简单

public class ThreadPoolTest3 {

    public static void main(String[] args) {
        ExecutorService executorService1 = Executors.newCachedThreadPool();
        ExecutorService executorService2 = Executors.newFixedThreadPool(10);
        ExecutorService executorService3 = Executors.newSingleThreadExecutor();
        for (int i = 1; i <= 100; i++) {
            executorService1.execute(new TestRunnable(i));
//            executorService2.execute(new TestRunnable(i));
//            executorService3.execute(new TestRunnable(i));
        }
    }

    static class TestRunnable implements Runnable {
        int i = 0;

        public TestRunnable(int i) {
            this.i = i;
        }


        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
1.newCachedThreadPool

newCachedThreadPool方法如下:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

它和直接创建线程类似,可以创建无限的线程

2.newFixedThreadPool

newFixedThreadPool方法如下:

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

它拥有指定大小的核心线程数,并且只有核心线程,以及一个无限大小的任务队列

3.newSingleThreadExecutor

newSingleThreadExecutor方法如下:

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

它仅有一个核心线程,以及一个无限大小的任务队列,所以相当于单线程执行任务队列中的任务

相关文章

  • Java--线程池

    线程池(Thread Pool):顾名思义,就是类似于一个充满了线程的池子,它其实是一种线程的使用模式,是一种池化...

  • Java--线程池

    Java中提供了一系列和锁相关的工具:synchronized、ReentrantLock、AtomicXXX类,...

  • java线程池

    线程VS线程池 普通线程使用 创建线程池 执行任务 执行完毕,释放线程对象 线程池 创建线程池 拿线程池线程去执行...

  • Java--线程

    线程 线程(Thread)是操作系统中能够调度的最小单位,被包含在进程之中,是进程的实际运作单位,一条线程是...

  • Java--线程

    线程是什么?在计算机设计初期,并没有线程的概念,只有进程,linux系统下没有真正意义的线程,只有进程,c语言中的...

  • java----线程池

    什么是线程池 为什么要使用线程池 线程池的处理逻辑 如何使用线程池 如何合理配置线程池的大小 结语 什么是线程池 ...

  • Java线程池的使用

    线程类型: 固定线程 cached线程 定时线程 固定线程池使用 cache线程池使用 定时调度线程池使用

  • Spring Boot之ThreadPoolTaskExecut

    初始化线程池 corePoolSize 线程池维护线程的最少数量keepAliveSeconds 线程池维护线程...

  • 线程池

    1.线程池简介 1.1 线程池的概念 线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性...

  • 多线程juc线程池

    java_basic juc线程池 创建线程池 handler是线程池拒绝策略 排队策略 线程池状态 RUNNIN...

网友评论

    本文标题:Java--线程池

    本文链接:https://www.haomeiwen.com/subject/gxfmoltx.html