线程池初步学习

线程池初步学习

文章地址:https://blog.csdn.net/renhuan28/article/details/85322844

为什么使用线程池?

​ 在我们的日常开发中,难免会使用到线程,部分还会用到多线程并发问题。我们知道,线程的创建和释放,需要占用不小的内存和资源。如果每次需要使用线程时,都new 一个Thread的话,难免会造成资源的浪费,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。不利于扩展,比如如定时执行、定期执行、线程中断,所以很有必要了解下ExecutorService的使用。

Java通过Executors提供四种线程池,分别为:
    //一个定长线程池,支持定时及周期性任务执行
    ExecutorService executorServiceScheduled = Executors.newScheduledThreadPool(1);

    //一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    ExecutorService executorServiceCached = Executors.newCachedThreadPool();

    //一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
    ExecutorService executorServiceSingle = Executors.newSingleThreadExecutor();

    //一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    ExecutorService executorServiceFixed = Executors.newFixedThreadPool(5);
常见参数:
corePoolSize : 核心线程数,一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;如果没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。

maximumPoolSize : 最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。

keepAliveTime : 也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。

unit : 时间单位,TimeUnit.SECONDS等。

workQueue : 任务队列,存储暂时无法执行的任务,等待空闲线程来执行任务。

threadFactory :  线程工程,用于创建线程。
线程池的执行:

有两种方式 submitexecute ,这两者的区别是:

1 接收的参数不一样;execute()的入参为Runnable ,submit ()的入参为Runnable 或Callable 。

​ (补充说明:Callable与Runnable类似,也是创建线程的一种方式,实现其call()方法即可,方法可以有返回值,而且方法上可以抛出异常;)

2 submit()有返回值,返回值为 Future ,而execute()没有;

3 submit()可以进行Exception处理; 通过对Future.get()进行抛出异常的捕获

以 FixedThreadPool 为例写个demo
public static void main(String[] args) {

    List<Future<String>> futures = Lists.newArrayList();

    int num = Runtime.getRuntime().availableProcessors();

    //一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    ExecutorService executorServiceFixed = Executors.newFixedThreadPool(num);
  
    //遍历启动线程池里的线程,执行用submit,入参用Callable
    for (int i = 0; i < num; i++) {
      Future<String> submit = executorServiceFixed.submit(new UserinfosTaskByCall(i));
      futures.add(submit);
    }
	//打印返回值
    for (Future future : futures) {
      try {
        System.out.println(future.get());
      } catch (InterruptedException e) {
        e.printStackTrace();
      } catch (ExecutionException e) {
        e.printStackTrace();
      } finally {
        executorServiceFixed.shutdown();
      }
    }

    while (true) { }
}

入参Callable 任务

public class UserinfosTaskByCall implements Callable {

  int b = 0;
  public UserinfosTaskByCall(int i) {
    b = i;
  }

  @Override
  public Object call() throws Exception {
    System.out.println("开始干活了,+++++++++++++" + Thread.currentThread().getName());
    try {
      Thread.sleep(5000);
      if (b == 2) {
        throw new InterruptedException("yichang");
      }
    } catch (InterruptedException e) {
      return "有异常";
    }
    return "结束啦";
  }
}
对ExecutorService的封装 CompletionService

CompletionService整合了Executor和BlockingQueue的功能。

ExecutorService要等所有submit对象执行完,才会返回get值;

CompletionService只要notEmpty,就会返回get值.(阻塞针对的是尾部full会阻塞,头部empty会阻塞),CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,直到有完成的Future对象加入到Queue中。所以,先完成的必定先被取出。这样就减少了不必要的等待时间

CompletionService接口定义了一组任务管理接口:
submit() - 提交任务
take() - 获取任务结果
poll() - 获取任务结果

方法poll()的作用是获取并移除最新已完成的任务的Future,如果不存在这样的任务,则返回null,方法poll()不像take()方法那样具有阻塞的效果,立即调用立即返回结果。

BlockingQueue的核心方法

放入数据:
  offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,则返回true,否则返回false.(本方法不阻塞当前执行方法的线程)
  offer(E o, long timeout, TimeUnit unit),可以设定等待的时间,如果在指定的时间内,还不能往队列中加入BlockingQueue,则返回失败。
  put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.
  
获取数据:
  poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null;
  poll(long timeout, TimeUnit unit):从BlockingQueue取出一个队首的对象,如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。
  take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入;
  drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数), 通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值