Java:线程池基础

基本认识

以下摘自百度百科

线程池的概念:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。

线程池的作用:在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些”池化资源”技术产生的原因。比如大家所熟悉的数据库连接池正是遵循这一思想而产生的。

总而言之:线程池就是系统通过池化资源的概念,达到节省资源的一种手段。

关于线程与任务:《Java变成思想第四版》里面描述得很清楚,一言概之:线程是用来驱动任务的。初学时以为一个线程就是一个任务,其实不然。


Java线程池

Java有四种常见的线程池,在java.util.concurrernt.Executors类里面以静态工厂形式返回:

  • newCachedThreadPool:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。(Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available.)
  • newFixedThreadPool: 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。(Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue.)
  • newScheduledThreadPool:创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。( Creates a thread pool that can schedule commands to run after a given delay, or to execute periodically.)
  • newSingleThreadExecutor: 创建一个使用单个线程的线程池,以无界队列方式来运行该线程。(Creates an Executor that uses a single worker thread operating off an unbounded queue.)

观察这几个静态工厂,对于newCachedThreadPool、newSingleThreadExecutor,API客户端直接调用即可;而对于newFixedThreadPool,客户端需要关注参数:int nThreads;newScheduledThreadPool,客户端需要关注参数:int corePoolSize。

于是我们来看看源码里面这些参数都代表什么意思

以创建CachedThreadPool为例:

ExecutorService ctp = Executors.newCachedThreadPool();

newCachedThreadPool()方法的源码如下:

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

我们直接来到ThreadPoolExecutor类,看下几个构造器的参数都代表什么意思。

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

由于暂时只为入门的基础认识,所以后面几个参数我们不关心,姑且认为他们是线程池底层操作相关的参数,我们只看前面四个甚至只看前面两个API客户端会使用的参数。

源码文件的截图:

  • corePoolSize:常驻线程池的线程数
  • maximunPoolSize:线程池中允许的最多线程数
  • keepAliveTime:当现有线程数多于常驻的数目时,空闲线程若在这个最大时长还没等待到新任务,则被终止。
  • unit:keepAliveTimed的时间单位

再来看一下创建几种线程池时前面几个参数默认的都是啥:

1、Cached线程池:可以看到常驻的数目为0,最大的线程数目可以达到很大,当线程空闲时60秒内没有新任务则终止。从名字就可以看出这个效果了。

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

2、Fixed线程池:可以看到常驻的数目由客户端决定,最大的线程数目同样也是,当线程空闲时则马上被终止。

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

3、Single线程池:只有也只能有一条工作线程,没有任务就终止。

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

4、Scheduled线程池:常驻线程数由客户端决定

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

简单实例

public class Test {

    private static ExecutorService cached = Executors.newCachedThreadPool();
    private static ExecutorService fixed = Executors.newFixedThreadPool(3);
    private static FileWriter fw;

    static {
        try {
            fw = new FileWriter("j:/log.txt");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        Runnable sellingApple = new Selling("apple");
        Runnable sellingBanana = new Selling("banana");
        for (int i = 0; i < 5; i++) {
            cached.execute(sellingApple);
        }
        cached.execute(sellingBanana);
    }

    private static class Selling implements Runnable {
        private int count = 500;
        private String item;

        public Selling(String item) {
            this.item = item;
        }

        public void run() {
            // TODO Auto-generated method stub
            while (true) {
                synchronized (this) {
                    if (count < 1) {
                        break;
                    }
                    sell();
                }
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            cached.shutdown();
        }

        private void sell() {
            try {
                fw.write(Thread.currentThread().getName() + " sold " + item
                        + ": #" + count-- + "\n");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

结果:我们总共在缓存线程池里面开启了6条线程,前面5条是用来驱动卖苹果这个任务,最后的第6条用来卖香蕉。

假设把上面主程序的cached线程池换成参数为3的fixed线程池,结果如下:为什么必须是3条线程卖完苹果后才能卖香蕉呢?我们开了5条线程去卖苹果,和1条线程去卖香蕉,但是fixed线程池最多只能有3条线程,所以卖香蕉这个任务在队列里面等待着。等这3条线程卖完全部苹果后,就会复用其中1条线程去卖香蕉了。如果我们把参数改为6,则和上述结果一样。



小结:不同线程池作用不同,execute开启一条线程去驱动一个任务,shutdown()关闭线程池回收资源,但任务还是会按队列顺序执行完(Initiates an orderly shutdown in which previously submitted tasks are executed),当前线程数不够驱动任务时,任务就在队列等着。

时间: 2024-10-06 00:10:21

Java:线程池基础的相关文章

Java线程池-基础

线程池的核心类:ThreadPoolExecutor ,类图如下: (I:代表接口,C:代表实现类) I:Executor I:ExecutorService C:AbstractExecutorService C:ThreadPoolExecutor 构造函数: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Run

Java(Android)线程池---基础篇

1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? 1 newThread(newRunnable(){ 2 3 @Override 4 publicvoidrun(){ 5 // TODO Auto-generated method stub 6 } 7 }).start(); 那你就out太多了,new Thread的弊端如下: a. 每次new Thread新建对象性能差.b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致

Java线程池与java.util.concurrent

Java(Android)线程池 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }).start(); 那你就out太多了,new Thre

Java 线程池(一):开篇及Executor整体框架介绍

一.开篇 线程池.数据库连接池,在平时的学习中总能接触到这两个词,但它们到底是什么?和线程,数据库连接有什么关系?为什么需要“池”?“池”的概念及作用是什么?要弄清楚这些问题,就要深入到“池”的实现中去. 之前找实习工作时,时常有面试官问这类问题,自己平时知道如何使用Java的Executor线程池框架,但是具体的细节还真没感受,所以打算开始试着研究一下Executor线程池框架. 废话到此打住,正式开始! 二.Executor整体框架 让我们先站在一个较高的角度俯视一下Java线程池的整体结构

【java线程系列】java线程系列之java线程池详解

一线程池的概念及为何需要线程池: 我们知道当我们自己创建一个线程时如果该线程执行完任务后就进入死亡状态,这样如果我们需要在次使用一个线程时得重新创建一个线程,但是线程的创建是要付出一定的代价的,如果在我们的程序中需要频繁使用线程,且每个线程执行的时间很短,短到几乎小于线程创建及销毁的时间那么代价将会更大,如:服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的.显然如果频繁的创建销毁线程效率将非常低. 那么我们能否让一个线程可以复用,即当一个线程执行完后不销毁该线程,而

07深入理解Java线程池

之前面试baba系时遇到一个相对简单的多线程编程题,即"3个线程循环输出ADC",自己答的并不是很好,深感内疚,决定更加仔细的学习<并发编程的艺术>一书,到达掌握的强度.(之前两月休息时间都花在了lol和吃鸡上,算是劳逸结合了,推荐大家代码写累了可以玩下吃鸡,是个不错的调剂) 流程分析 Java的线程池是最常用的并发框架,合理的使用线程池可以降低系统消耗.提高响应速度.提高线程的可管理性.线程池的基础处理流程如下图所示. 上图中标红的4处正好是构建线程池的核心,核心线程池大

Java——线程池

构造一个线程池为什么需要几个参数?如果避免线程池出现OOM?Runnable和Callable的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段. 基础知识 Executors创建线程池 Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThreads),但是便捷不仅隐藏了复杂性,也为我们埋下了潜在的隐患(OOM,线程耗尽). Executors创建线程池便捷方法列

Java线程池原理

转自:https://www.jianshu.com/p/a166944f1e73 本篇文章主要介绍Java线程池的原理以及源码的分析 线程池的介绍 Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池. 线程池的优点 第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行. 第三:提高线程的可管理性.线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会

Java 线程池原理分析

1.简介 线程池可以简单看做是一组线程的集合,通过使用线程池,我们可以方便的复用线程,避免了频繁创建和销毁线程所带来的开销.在应用上,线程池可应用在后端相关服务中.比如 Web 服务器,数据库服务器等.以 Web 服务器为例,假如 Web 服务器会收到大量短时的 HTTP 请求,如果此时我们简单的为每个 HTTP 请求创建一个处理线程,那么服务器的资源将会很快被耗尽.当然我们也可以自己去管理并复用已创建的线程,以限制资源的消耗量,但这样会使用程序的逻辑变复杂.好在,幸运的是,我们不必那样做.在