Java线程池:Executor

Java–线程池

一系列和锁相关的工具:
  • synchronized、ReentrantLock、AtomicXXX类,

之前我们也提到了内核线程模型,Hotspot虚拟机线程的创建都是通过内核线程提供的轻量级进程API创建的,反复的创建线程会不断的由OS线程调度,这也会消耗大量CPU性能

争对创建线程,除了使用Thread之外,Java还提供了线程池类,

  • 它们最终都实现的Executor接口,JDK1.7中的继承实现关系如下:

img

一、线程池与Thread

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

        //当前时间
		long startTime = System.currentTimeMillis();
		//创建 10000 一万个 线程,存储
        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));
Thread-0
Thread-1
Thread-2 #线程挨个执行。这是加入 t.getName()打印的
ms:1 

ms:612 # 1万个
  • countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
  • 是通过一个计数器来实现的,计数器的初始值是线程的数量。
    • 每当一个线程执行完毕后,计数器的值就-1,
    • 当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

使用线程池:

        //创建只有一个线程的线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
            }
        });
        executorService.shutdown();
        System.out.println("我是下面的执行");

我是下面的执行
0
1
  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() {
                    //进行减,每次执行完毕,就 --1
                    countDownLatch.countDown();
                }
            });
        }

        try {
            //如:7304,说明才 执行2700个线程
            System.out.println(countDownLatch.getCount());
            countDownLatch.await();//等待10000计数结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //关闭线程
        executorService.shutdown();
        System.out.println("ms:" + (System.currentTimeMillis() - startTime));
    }

ms:12

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

二、ThreadPoolExecutor
1.ThreadPoolExecutor执行任务图解

Executors是个帮助类,内部实际使用的是ThreadPoolExecutor,构造函数如下:

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

ThreadPoolExecutor大致的执行场景如下:

img

2.ThreadPoolExecutor参数

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

  • corePoolSize:核心线程最大数,当有新任务时,

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

    • 非核心线程最大数 = 最大线程数 - 核心线程最大数,当核心线程全部创建并都在执行任务,那么新任务使用非核心线程执行,同样使用核心线程创建机制:创建线程或使用空闲的线程,非核心线程可以指定死亡时间,相当于临时工
  • keepAliveTime:非核心线程的保持存活时间,当一个非核心线程持续keepAliveTime时间都处于空闲状态,那么使它消亡

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

  • workQueue:任务队列,当线程总数到达最大,并且所有线程都有任务在执行,那么新任务可以进入该队列,等待有线程空闲时执行队列中的任务。

    • 通过Executors创建的任务队列,都没有最大值,所以当遇到服务器并发量大的情况,容易OOM,安卓开发中不需要考虑这个问题
  • threadFactory:创建线程工厂,一般使用默认的即可

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

Excutors 创建线程池的方法最终都是通过 ThreadPoolExcutor 创建出来的。

  • corePoolSize 线程池始终活跃的线程数。
  • maximumPoolSize 线程池最大能创建的线程数。
  • keepAliveTime 始终活跃线程之外的 线程空闲之后存活的最长时间。
  • unit 存活时间单位。
  • workQueue 当线程池工作饱和时,新来的任务存放的队列。
Excutors 线程池的参数 2
  • 创建指定大小的线程池

    public static ExecutorService newFixedThreadPool(int nThreads)
    
  • 创建无限大小的线程池,初始大小为0

    public static ExecutorService newCachedThreadPool()
    
  • 创建无限大小的线程池,初始大小指定

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    
  • 创建只有一个线程的线程池

    public static ExecutorService newSingleThreadExecutor()
    
3.任务队列

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

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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值