线程池原理&常用四大线程池及七大参数

目录

前言

最近在面试,关于一些多线程的内容,细致的部分很容易就想不起来具体应该怎么回答。翻开之前的笔记本,感觉就在眼前,知识不去实践,就像是别人的。尤其是程序猿这行,源源不断的新技术,就不要做捡了芝麻丢了西瓜的人。
总结在博客上,也能再温习一下。用不用的先门清~

常用的四种线程池

newCachedThreadPool——可缓存线程池

描述:

  • 如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

特点

  • 线程的创建数量几乎没有限制。这样可灵活的往线程池中添加线程。
  • 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程
  • 要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class executor {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            try {
                Thread.sleep(index*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            pool.execute(new Runnable() {
                public void run() {
                    System.out.println(index);
                }
            });
        }
    }
}

《线程池原理&常用四大线程池及七大参数》

newFixedThreadPool————指定线程数量

描述:

  • 创建一个指定工作线程数量的线程池。
  • 每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

优点:

  • 提高程序效率和节省创建线程时所耗的开销

缺点:

  • 线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class fixedThreadPool {
    public volatile int i;
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);  
        for (int i = 0; i < 10; i++) {
            final int index = i;
            fixedThreadPool.execute(new Runnable() {
                public void run() {
                    try {
                        System.out.println(index);
                        Thread.sleep(8000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

运行结果:两个线程一起执行,隔8s切换
《线程池原理&常用四大线程池及七大参数》

newSingleThreadExecutor————单线程的Executor

描述:

  • 只创建唯一的线程来执行任务。保证任务按照指定顺序(FIFO,LIFO,优先级)执行。
  • 如果这个线程异常结束,会有另一个取代它
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class singleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            singleThreadExecutor.execute(new Runnable() {
                public void run() {
                    try {
                        System.out.println(index);
                        Thread.sleep(8000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}


运行:单线程运行
《线程池原理&常用四大线程池及七大参数》

newScheduleThreadPool——定时线程池

描述:

  • 支持定时及周期性任务执行。

栗子:

  • 延迟3s执行示例代码
import java.sql.Time;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class scheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
       scheduledThreadPool.schedule(new Runnable() {
           public void run() {
               System.out.println("延迟5秒执行");
           }
       },5, TimeUnit.SECONDS);
    }
}

《线程池原理&常用四大线程池及七大参数》

栗子:

  • 延迟2秒后每5秒执行一次,定期执行示例代码
import java.sql.Time;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class scheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
       scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
           public void run() {
               System.out.println("延迟5秒执行");
           }
       },2,5,TimeUnit.SECONDS);
    }
}

运行:
《线程池原理&常用四大线程池及七大参数》

线程池七大参数

corePoolSize——核心线程最大数

maximumPoolSize——线程池最大线程数

keepAliveTime——空闲线程存活时间。

  • 当一个非核心线程被创建,使用完归还给线程池
  • 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由
    keepAliveTime来设定

unit——空闲线程存活时间单位

  • 这是keepAliveTime的计量单位

workQueue——等待队列

  • 当线程池满了,线程就会放入这个队列中。任务调度再取出

jdk提供四种工作队列

  1. ArrayBlockingQueue——基于数组的阻塞队列,按FIFO,新任务放队尾
    • 有界的数组可以防止资源耗尽问题。
    • 当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。
    • 如果队列已经是满的,则创建一个新线程,
    • 如果线程数量已经达到maxPoolSize,则会执行拒绝策略
  2. LinkedBlockingQuene——基于链表的无界阻塞队列,按照FIFO排序
    • 其实最大容量为Interger.MAX
    • 由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,
    • 因此使用该工作队列时,参数maxPoolSize其实是不起作用的
  3. SynchronousQuene——不缓存任务的阻塞队列
    • 生产者放入一个任务必须等到消费者取出这个任务。
    • 也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,
    • 如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略
  4. PriorityBlockingQueue——具有优先级的无界阻塞队列
    • 优先级通过参数Comparator实现。

threadFactory

  • 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等

handler——拒绝策略

当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,就用到拒绝策略
jdk中提供了4中拒绝策略

  1. CallerRunsPolicy——主线程自己执行该任务
    • 该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,否则直接抛弃任务。
  2. AbortPolicy——抛出异常
    • 该策略下,直接丢弃任务,并抛出RejectedExecutionException异常
  3. DiscardPolicy——直接丢弃
    • 该策略下,直接丢弃任务,什么都不做
  4. DiscardOldestPolicy——早删晚进
    • 该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列

线程池工作流程

  • 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
  • 调用 execute() 方法添加一个任务时,线程池会做如下判断:
    • 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
    • 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
    • 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
    • 如果队列满了,而且正在运行的线程数量等于 maximumPoolSize,那么线程池会抛出异常 RejectExecutionException。
  • 当一个线程完成任务时,它会从队列中取下一个任务来执行。
  • 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

《线程池原理&常用四大线程池及七大参数》

    原文作者:UlricaQ
    原文地址: https://blog.csdn.net/Carrie_Q/article/details/108696481
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞