【Java线程池】Java线程池汇总,看这一篇文章就够了-1

【Java线程池】Java线程池汇总,看这一篇文章就够了

 

(1)引言1:Java线程池

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

(2)引言2:Executors 类

为了方便, Java 5建议使用 Executors 工具类来得到 Executor 接口的具体对象,需要注意的是 Executors 是一个类,不是 Executor 的复数形式。 Executors 提供了以下一些 static 的方法:

  • callable(Runnable task):将 Runnable 的任务转化成 Callable 的任务。
  • newFixedThreadPool(int poolSize) :产生一个 ExecutorService 对象,这个对象带有一个大小为 poolSize 的线程池,若任务数量大于 poolSize ,任务会被放在一个 queue 里顺序执行。
  • newCachedThreadPool( ):产生一个 ExecutorService 对象,这个对象带有一个线程池,线程池的大小会根据需要自动调整,线程执行完任务后将会返回线程池,供执行下一次任务使用。
  • newSingleThreadExecutor( ):产生一个 ExecutorService 对象,这个对象只有一个线程可用来执行任务,若任务多于一个,任务将按先后顺序执行。
  • newSingleThreadScheduledExecutor :产生一个 ScheduledExecutorService 对象,这个对象的线程池大小为 1 ,若任务多于一个,任务将按照先后顺序执行。
  • newScheduledThreadPool(int poolSize):产生一个 ScheduledExecutorService 对象,这个对象的线程池大小为 poolSize ,若任务数量大于 poolSize ,任务会在一个 queue 里等待执行。

(3)本文讲解一下几个常用方法:

  • newFixedThreadPool
  • newCachedThreadPool
  • newScheduledThreadPool
  • newSingleThreadExecutor
  • newSingleThreadScheduledExecutor
  • 例外:ThreadPoolExecutor

【Java线程池】1-newFixedThreadPool

(1) JDK方法:

创建一个可重用的固定线程数量的线程池,以共享的无界队列方式来运行这些线程。ExecutorService threadPool = Executors.newFixedThreadPool(3);    // 创建可以容纳3个线程的线程池

注意:创建一个固定大小的线程池。每次提交一个任务就会创建一个线程,直到创建的线程数量达到线程池的最大大小nThreads。线程池的大小一旦达到最大值就会保持不变。如果在所有线程都处于活动状态时,这时再有其他任务提交,他们将等待队列中直到有空闲的线程可用。如果任何线程由于执行过程中的故障而终止,将会有一个新线程将取代这个线程执行后续任务。

(2)举例如下:

package com.storm_03;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @Author: chenmengmeng
 * @Date: 2018/10/25 11:45
 * @Description:
 */
public class fixedThreadPoolTest {
    public static void main(String[] args) {
        //创建线程池,容量是3
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        //主线程创建4个任务
        for (int i = 1; i < 5; i++) {
            final int taskID = i;
            //任务加入线程池,让线程池内的线程去执行每一个任务
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(500);
                            Thread.sleep(500); //为了测试出效果,让每次任务执行都需要一定时间
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        //任务执行结束后打印一句话
                        System.out.println("第" + taskID + " 个任务的第" + i + " 次执行");
                    }
                }
            });
        }
        threadPool.shutdown();
        System.out.println("主线程执行结束");
    }
}

(3)执行结果:

(4)结果分析:上段代码中,创建了一个固定大小的线程池,容量为3,然后循环执行了4个任务。由输出结果可以看到,前3个任务首先执行完,然后空闲下来的线程去执行第4个任务。在FixedThreadPool中,有一个固定大小的池,如果当前需要执行的任务超过了池大小,那么多余出来的任务会进入等待状态,直到线程池中有空闲下来的线程才去执行这些多余的任务,而当执行的任务数量小于线程池大小,空闲的线程也不会被销毁,而是处于随机待命状态。

【Java线程池】2-newCachedThreadPool

(1) JDK方法:

创建一个 可以根据需要 自动创建新线程的线程池,但是,在以前创建好的线程可用时 将重用它们。ExecutorService threadPool = Executors.newCachedThreadPool();    // 线程池的大小会根据执行的任务数量动态分配  

注意:创建一个可缓存的线程池。在JAVA文档中是这样介绍可回收缓存线程池的:创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可以提高程序性能。如果线程池中有线程可用,那么调用 execute 将重用以前构造的线程;如果线程池中没有可用的线程,则创建一个新线程并添加到线程池中。此线程池不会对线程池的大小做限制,线程池大小完全依赖操作系统或者说JVM所能够创建的最大线程数量大小。此线程池会终止并从缓存中移除那些已有 60 秒钟(默认)未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。(可以通过java.util.concurrent.ThreadPoolExecutor 类的构造方法构造更加具体的类,例如指定时间参数等)。创建线程本身需要很多资源,包括内存,记录线程状态,以及控制阻塞等等。因此,相比另外两种线程池,在需要频繁创建短期异步线程的场景下,newCachedThreadPool能够复用已完成而未关闭的线程来提高程序性能。通俗讲就是:CachedThreadPool会创建一个缓存区,将初始化的线程缓存起来,如果线程有可用的,就使用之前创建好的线程,如果没有可用的,就会新创建线程。另外,终止并且从缓存中移除已有60秒未被使用的线程。

(2)举例如下:

package com.storm_03;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @Author: chenmengmeng
 * @Date: 2018/10/25 11:45
 * @Description:
 */
public class cachedThreadPoolTest {
    public static void main(String[] args) {
        //创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
        //对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。
        ExecutorService threadPool = Executors.newCachedThreadPool();
        //主线程创建4个任务
        for (int i = 1; i < 5; i++) {
            final int taskID = i;
            //任务加入线程池,让线程池内的线程去执行每一个任务
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(500);
                            Thread.sleep(500); //为了测试出效果,让每次任务执行都需要一定时间
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        //任务执行结束后打印一句话
                        System.out.println("第" + taskID + " 个任务的第" + i + " 次执行");
                    }
                }
            });
        }
        threadPool.shutdown();
        System.out.println("主线程执行结束");
    }
}

(3)执行结果:

(4)结果分析:可见,4个任务是交替执行的,CachedThreadPool会创建一个缓存区,将初始化的线程缓存起来,如果线程有可用的,就使用之前创建好的线程,如果没有可用的,就会新创建线程。另外,终止并且从缓存中移除已有60秒未被使用的线程。

【Java线程池】3-newSingleThreadExecutor

(1) JDK方法:

创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。ExecutorService threadPool = Executors.newSingleThreadExecutor();    // 创建只含有单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程去替代它从而继续执行任务。

注意:创建一个单线程的线程池。这个线程池中只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么就会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

(2)举例如下:

package com.storm_03;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * @Author: chenmengmeng
 * @Date: 2018/10/25 11:45
 * @Description:
 */
public class singleThreadExecutorTest {
    public static void main(String[] args) {
        //得到的是一个单个的线程,这个线程会保证你的任务执行完成,如果当前线程意外终止,会创建一个新线程继续执行任务.
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //主线程创建4个任务
        for (int i = 1; i < 5; i++) {
            final int taskID = i;
            //任务加入线程池,让线程池内的线程去执行每一个任务
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 5; i++) {
                        try {
                            Thread.sleep(500);
                            Thread.sleep(500); //为了测试出效果,让每次任务执行都需要一定时间
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        //任务执行结束后打印一句话
                        System.out.println("第" + taskID + " 个任务的第" + i + " 次执行");
                    }
                }
            });
        }
        threadPool.shutdown();
        System.out.println("主线程执行结束");
    }
}

(3)执行结果:

(4)结果分析:4个任务是顺序执行的,SingleThreadExecutor得到的是一个单个的线程,这个线程会保证你的任务执行完成,如果当前线程意外终止,则会创建一个新的线程继续执行该任务,这和我们直接创建线程不同,也和newFixedThreadPool(1)不同。

【Java线程池】4-newScheduledThreadPool

(1) JDK方法:

创建一个可安排在给定延迟时间后 运行的或者可定期地执行的线程池。ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);    // 效果类似于Timer定时器  

注意:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

(2)举例如下:

package com.storm_03;
import java.util.Calendar;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * @Author: chenmengmeng
 * @Date: 2018/10/25 13:18
 * @Description:
 */
public class scheduledThreadPoolTest {
    public static void main(String[] args) {
        //创建线程池
        ScheduledExecutorService schedulePool = Executors.newScheduledThreadPool(1);
        //5秒后执行任务
        schedulePool.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Calendar.getInstance().getTime());
                System.out.println("爆炸");
            }
        }, 5, TimeUnit.SECONDS);
        //5秒后执行任务,以后每2秒执行一次
        schedulePool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.print(Calendar.getInstance().getTime());

                System.out.println(" ---爆炸!!");
            }
        }, 5, 2, TimeUnit.SECONDS);

    }
}

(3)执行结果:

(4)结果分析:ScheduledThreadPool 可以定时的或延时的执行任务。

 

【Java线程池】5-ThreadPoolExecutor

(1)无论创建哪种线程池,必须要调用 ThreadPoolExecutor类。线程池类是 java.util.concurrent.ThreadPoolExecutor,常用构造方法为: 

  • ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) 
    • corePoolSize: 线程池维护核心线程的最少数量 
    • maximumPoolSize:线程池维护线程的最大数量 
    • keepAliveTime: 线程池维护线程所允许的空闲时间 
    • unit: 线程池维护线程所允许的空闲时间的单位 
    • workQueue: 线程池所使用的缓冲队列 
    • handler: 线程池对拒绝任务的处理策略 

(2)一个任务通过 execute(Runnable r ) 方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

(3)当一个任务通过execute(Runnable)方法 欲添加到线程池时: 

  • 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。 
  • 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。 
  • 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。 
  • 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。 

也就是说:处理任务的优先级为: 

  • 核心线程池corePoolSize、任务队列workQueue、最大线程maximumPoolSize,若三者都满了,使用handler处理被拒绝的任务。 
  • 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。 

(4)unit 可选的参数为java.util.concurrent.TimeUnit中的几个静态属性: 
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。 
(5)workQueue 常用的可以是:java.util.concurrent.ArrayBlockingQueue
(6)handler有四个选择: 

  • ThreadPoolExecutor.AbortPolicy() :抛出java.util.concurrent.RejectedExecutionException异常 
  • ThreadPoolExecutor.CallerRunsPolicy() :重试添加当前的任务,他会自动重复调用execute()方法 
  • ThreadPoolExecutor.DiscardOldestPolicy() :抛弃旧的任务 
  • ThreadPoolExecutor.DiscardPolicy() :抛弃当前的任务 

当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。来源:https://www.cnblogs.com/baizhanshi/p/5469948.html

******************************************************************

(5-1)FixedThreadPool运行图如下:

执行过程如下:

1.如果当前工作中的线程数量少于corePool的数量,就创建新的线程来执行任务。

2.当线程池的工作中的线程数量达到了corePool,则将任务加入LinkedBlockingQueue。

3.线程执行完1中的任务后会自动从队列中取任务去执行。

注意:LinkedBlockingQueue是无界队列,所以可以一直添加新任务到线程池。

(5-2)SingleThreadExecutor运行图如下:

 

执行过程如下:

1.如果当前工作中的线程数量少于corePool的数量,就创建一个新的线程来执行任务。

2.当线程池的工作中的线程数量达到了corePool,则将任务加入LinkedBlockingQueue。

3.线程执行完1中的任务后会从队列中取任务去执行。

注意:LinkedBlockingQueue是无界队列,所以可以一直添加新任务到线程池。由于在线程池中只有一个工作线程,所以任务可以按照添加顺序执行。

(5-3)CachedThreadPool运行图如下:

执行过程如下:

1.首先执行SynchronousQueue.offer(Runnable task)。如果在当前的线程池中有空闲的线程正在执行SynchronousQueue.poll(),那么主线程执行的offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方法执行成功,否则执行步骤2。

2.当线程池为空(初始maximumPool为空)或没有空闲线程时,配对失败,将没有线程执行SynchronousQueue.poll操作。这种情况下,线程池会创建一个新的线程执行任务。

3.在创建完新的线程以后,将会执行poll操作。当步骤2的线程执行完成后,将等待60秒,如果此时主线程提交了一个新任务,那么这个空闲线程将执行新任务,否则被回收。因此长时间不提交任务的CachedThreadPool不会占用系统资源。

SynchronousQueue是一个不存储元素阻塞队列,每次要进行offer操作时必须等待poll操作,否则不能继续添加元素。

 

最后是各种阻塞队列的说明和比较:

Java并发包中的阻塞队列一共是7个,当然它们都是线程安全的。 

1.  ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。 

2.  LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。 

3.  PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。 

4.  DealyQueue:一个使用优先级队列实现的无界阻塞队列。 

5.  SynchronousQueue:一个不存储元素的阻塞队列。 

6.  LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。 

7.  LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

 

  • 47
    点赞
  • 365
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值