美文网首页
线程池的使用

线程池的使用

作者: topshi | 来源:发表于2019-04-17 17:35 被阅读0次

前言

多线程软件设计可以最大限度地发挥现代多核处理器地计算能力,提高生产系统地吞吐量和性能。但是,若不加控制而是随意使用线程,系统的性能就会大打折扣。
那么,随意使用线程会存在什么问题呢?
真实生产环境下,可能会开启很多线程以支撑应用,而线程数量过大时,反而会耗尽CPU内存资源

  • 虽然与进程相比,线程比较轻量级,但创建和关闭线程需要花费时间。如果每个小任务都创建一个线程,可能创建和销毁线程的时间比任务工作的时间还长,得不偿失。
  • 线程本身需要占用内存资源,大量的线程会抢占内存资源,处理不当会导致out of memery,大量的线程回收会给GC带来很大的压力,延长GC的停顿时间
    因此,我们要使用线程池,控制线程的创建和销毁。

什么是线程池

为了避免系统频繁地创建和销毁线程,我们要对创建的线程进行复用。在线程池中,当你需要使用线程时,从线程池中随便拿一个空闲线程,完成工作后,线程不会被销毁,而是归还到线程池中以被其他人使用。通过这种方式,可以节约不少创建和销毁线程对象的时间。

使用线程池后,我们以往所理解的创建线程就是从线程池中获得空闲线程,处理完线程任务后的关闭线程任务就是往线程池归还线程。

JDK对线程池的支持

为了能够更好地控制多线程,JDK提供了一套Executor框架,帮助开发人员控制线程,其本质就是一个线程池。


Executor框架结构图

以上成员均来自J.U.C包中,是JDK并发包地核心类。ThreadPoolExecutor表示一个线程池。其次,还提供了一个Executors类,扮演着线程工厂地角色,通过Executors可以获得一个拥有特定功能的线程池。

Executors提供了以下获得各种类型线程池的工厂方法:

public static ExecutorService newFixedThreadPool(int var0) 
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newScheduledThreadPool(int var0)

以上工厂方法会返回具有不同工作特性的线程池,

  • newFixedThreadPool()方法:该方法返回一个固定线程数量的线程池,线程的数量不会改变。当一个新的任务提交,若池中有空闲线程,则立即执行,否则新的任务会被暂存至一个任务队列中,待有空闲任务则处理任务队列中的任务。
  • newSingleThreadExecutor()方法:该方法返回一个只有一个线程的线程池。若多于一个线程被提交,任务会被加入到任务队列,待线程空闲按照FIFO的顺序执行。
  • newCachedThreadPool()方法:该方法返回一个可以根据实际情况调整线程数量的线程池。线程池中的线程不是固定的,任务提交时,如果线程池中有空闲线程,则使用空闲线程执行线程任务,否则创建新的线程。
  • newSingleThreadScheduledExecutor()方法:该方法返回一个ScheduledExecutorService对象,线程池大小为1.ScheduledExecutorService接口在ExecutorService接口上扩展了在给定时间执行某任务的功能,如在某个固定的延时之后执行,或者周期性执行某个任务。
  • newScheduledThreadPool(int var0)方法:该方法返回一个ScheduledExecutorService对象,可以指定线程数量。

固定大小的线程池示例

/**
 * @Time : 2019/04/17 下午 04:19
 * @Author : xiuc_shi
 **/
public class ThreadPoolDemo {
    public static class MyTask implements Runnable{

        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + ":Thread ID:" + 
                               Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        MyTask task = new MyTask();//创建一个线程任务
        ExecutorService es = Executors.newFixedThreadPool(5);//创建一个线程池
        for(int i = 0;i < 10;i++){
            es.submit(task);//提交线程
        }
        es.shutdown();
    }
}
>结果
1555489886561:Thread ID:9
1555489886561:Thread ID:10
1555489886562:Thread ID:11
1555489886562:Thread ID:12
1555489886562:Thread ID:13
--------------------------前五个任务和后五个任务相差1000ms
1555489887561:Thread ID:10
1555489887561:Thread ID:9
1555489887562:Thread ID:12
1555489887562:Thread ID:11
1555489887562:Thread ID:13

计划任务
newScheduledThreadPool()方法返回一个ScheduledExecutorService对象,可以根据时间需要对线程进行调度。

主要方法

ScheduledFuture<?> schedule(Runnable var1, long var2, TimeUnit var4);
ScheduledFuture<?> scheduleAtFixedRate(Runnable var1, long var2, 
                                                  long var4, TimeUnit var6);
ScheduledFuture<?> scheduleWithFixedDelay(Runnable var1, long var2, 
                                                  long var4, TimeUnit var6);

ScheduledExecutorService不像其他线程池,它不一定会立即安排执行任务,而是起到计划执行的作用。它会在指定时间,对任务进行调度。

  • schedule()会在给定时间对任务进行一次调度。
  • scheduleAtFixedRate()scheduleWithFixedDelay()会对任务进行周期性的调度。
    1. FixedRate:从上一个任务的开始执行时间开始计时,经过var4时间调度下一个任务。
    2. FixedDelay:从上一个任务结束开始计时,经过var4时间,调度下一个任务。

scheduleAtFixedRate()调度示例
这个任务的执行时间为1秒钟,调度周期为2秒。

/**
 * @Time : 2019/04/17 下午 05:07
 * @Author : xiuc_shi
 **/
public class ScheduleAtFixedRateDemo {
    public static class MyTask implements Runnable{
        @Override
        public void run() {
            System.out.println(System.currentTimeMillis()/1000 +
                           ":Thread ID:" + Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
        ses.scheduleAtFixedRate(new MyTask(),0,2, TimeUnit.SECONDS);
    }
}
>结果,每隔2秒进行一次调度
1555492192:Thread ID:9
1555492194:Thread ID:9
1555492196:Thread ID:11
1555492198:Thread ID:9
1555492200:Thread ID:12
1555492202:Thread ID:11

如果任务的执行时间大于调度时间会怎样呢?我们设置Thread.sleep(8000);

>结果,结果是上一个任务执行完后马上调度下一个任务
1555493505:Thread ID:9
1555493513:Thread ID:9
1555493521:Thread ID:11
1555493529:Thread ID:9

注:调度程序不保证任务会无限期地持续调用,如果任务本身抛出异常,则后续的任务都会被中断,因此要妥善做好异常处理。

提交线程

public Future<?> submit(Runnable var1) {
    return this.e.submit(var1);
}
es.submit(线程任务);

public void execute(Runnable var1) {
    this.e.execute(var1);
}
es.execute(线程任务);

execute()没有返回值,如果不需要线程结果可使用execute方法,性能好;
submit()返回一个Future对象,如果需要得到线程结果则使用submit方法提交,且能在主线程中通过Future对象的get方法获得线程中的异常。
关闭线程池

public void shutdown() {
     this.e.shutdown();
}
es.shutdown();

不再接受新的任务,等待之前提交的任务执行完再关闭线程池。

public List<Runnable> shutdownNow() {
    return this.e.shutdownNow();
}
es.shutdownNow();

不再接受新的任务,试图停止池中的任务再关闭线程池,返回所有未处理的线程list集合。

相关文章

  • Java线程池的使用

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

  • java----线程池

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

  • spring 线程池和java线程池

    jdk线程池就是使用jdk线程工具类ThreadPoolExecutor 创建线程池spring线程池就是使用自己...

  • Spring @Async开启异步任务

    定义线程池 使用线程池

  • 八、线程池剖析

    一、前置问题 线程的状态转换 为什么要使用线程池 线程池的继承体系 线程池使用的场景 线程数的设置规则 线程池的状...

  • 1203-AsyncTask详解一:线程池的基本设置

    AsyncTask的内部使用线程池处理并发,要了解它是怎样使用线程池的,那要先了解线程池的基本设置 线程池的基本参...

  • ExecutorService shutdown()和shutd

    ExecutorService是我们经常使用的线程池,当我们使用完线程池后,需要关闭线程池。ExecutorSer...

  • java 线程池使用和详解

    线程池的使用 构造方法 corePoolSize:线程池维护线程的最少数量 maximumPoolSize:线程池...

  • java线程池

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

  • Android中的线程池

    前言 提到线程池,我们先说一下使用线程池的好处。使用线程池的优点可以概括为:1、重复使用线程池中的线程,避免因为线...

网友评论

      本文标题:线程池的使用

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