登录 |  注册
首页 >  编程语言 >  Java多线程与并发编程专题笔记 >  Spring线程池的创建及参数说明

Spring线程池的创建及参数说明

        在项目里面为了提高性能往往会在主线程里面开启一个新线程去执行,这种做法最方便快捷,但是当用户量数据上涨,很显然每次去开启新的线程服务器往往会吃不消,这时就需要线程池来管理和监控线程的状态。

创建多线程的三种方式

java 多线程很常见,如何使用多线程,如何创建线程,java 中有三种方式:

通过继承Thread接口

public class Mytheard1 extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println("thread#1===" + i);
			try {
				Thread.sleep(100L);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

通过实现Runnable接口

public class Mytheard2 implements Runnable {
	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println("thread#2===" + i);
			try {
				Thread.sleep(100L);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

通过实现Callable接口

public class Mytheard3 implements Callable<Integer> {

	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for (int i = 0; i < 30; i++) {
			System.out.println("thread#3===" + i);
			sum += i;
		}
		return sum;
	}
}

启动上面三个线程

public static void main(String[] args) throws InterruptedException {
		long startTime = System.currentTimeMillis();
		// 通过主线程启动自己的线程
		// 通过继承 thread 类
		Mytheard1 thread1 = new Mytheard1();
		thread1.start();
		// 通过实现 runnable 接口
		Thread thread2 = new Thread(new Mytheard2());
		thread2.start();
		// 通过实现 callable 接口
		Mytheard3 th = new Mytheard3();
		FutureTask<Integer> result = new FutureTask<>(th);
		new Thread(result).start();
		// 注意这里都不是直接调用 run() 方法,而是调运线程类 Thread 的 start 方法,在 Thread
		// 方法内部,会调运本地系统方法,最终会自动调运自己线程类的 run 方法
		// 让主线程睡眠
		Thread.sleep(1000L);
		System.out.println("主线程结束!用时:" + (System.currentTimeMillis() - startTime));
	}

上面三种方式更推荐通过实现 Runnable接口和实现 Callable接口,因为面向接口编程拓展性更好,而且可以防止 java 单继承的限制。


线程池的使用

        上面代码中可以直接新起线程,如果 100 个并发同时访问主线程也就是短时间就启动了 200 个线程,200 个线程同时工作,逻辑上是没有任何问题的,但是这样做对系统资源的开销很大。基于这样的考虑,就要考虑启用线程池,线程池里有很多可用线程资源,如果需要就直接从线程池里拿就是。当不用的时候,线程池会自动帮我们管理。

所以使用线程池主要有以下两个好处

  • 减少在创建和销毁线程上所花的时间以及系统资源的开销 

  • 如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存 。

自定义线程池

定义单例线程池:

public class ThreadPoolService {
    private static final int DEFAULT_CORE_SIZE=100;
    private static final int MAX_QUEUE_SIZE=500;
    private volatile static ThreadPoolExecutor executor; 

    private ThreadPoolService() {}; 

    // 获取单例的线程池对象
    public static ThreadPoolExecutor getInstance() {
        if (executor == null) {
            synchronized (ThreadPoolService.class) {
                if (executor == null) {
                    executor = new ThreadPoolExecutor(DEFAULT_CORE_SIZE,// 核心线程数
                    MAX_QUEUE_SIZE, // 最大线程数
                    Integer.MAX_VALUE, // 闲置线程存活时间
                    TimeUnit.MILLISECONDS,// 时间单位
                    new LinkedBlockingDeque<Runnable>(Integer.MAX_VALUE),// 线程队列
                    Executors.defaultThreadFactory()// 线程工厂
                    );
                }
            }
        }
        return executor;
    }

    public void execute(Runnable runnable) {
        if (runnable == null) {
            return;
        }
        executor.execute(runnable);
    }

    // 从线程队列中移除对象
    public void cancel(Runnable runnable) {
        if (executor != null) {
            executor.getQueue().remove(runnable);
        }
    }
}

创建线程池的主要参数说明:

corePoolSize(int):线程池中保持的线程数量,包括空闲线程在内。也就是线程池释放的最小线程数量界限。

maximumPoolSize(int): 线程池中嫩容纳最大线程数量。

keepAliveTime(long): 空闲线程保持在线程池中的时间,当线程池中线程数量大于 corePoolSize 的时候。

unit(TimeUnit枚举类): 上面参数时间的单位,可以是分钟,秒,毫秒等等。

workQueue(BlockingQueue): 任务队列,当线程任务提交到线程池以后,首先放入队列中,然后线程池按照该任务队列依次执行相应的任务。可以使用的 workQueue 有很多,比如:LinkedBlockingQueue 等等。

threadFactory(ThreadFactory类): 新线程产生工厂类。

handler(RejectedExecutionHandler类): 当提交线程拒绝执行、异常的时候,处理异常的类。该类取值如下:(注意都是内部类) 

ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException 异常。 

ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。

ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务,重复此过程。 

ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

获取线程并添加任务:

@Test
    public void threadPool() {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        Date startDate = new Date();
        System.out.println("开始时间:"+sf.format(startDate));
        for(int i=0;i<300000;i++){
            System.out.println("i=" + i);
            //启动线程
            ThreadPoolService.getInstance().execute(() -> {
                int total = 0;
                for(int k=0;k<1000;k++){
                    total = total + k;
                }
                System.out.println("total=" + total);
            });
        System.out.println("结束了");
        Date endDate = new Date();
        System.out.println("结束时间:"+sf.format(endDate));
        System.out.println("耗时,单位秒:"+ (endDate.getTime()-startDate.getTime())/1000);
      } }

JDK 提供的常用线程池

java 提供了几种常用的线程池,可以快捷的供程序员使用

  1. newFixedThreadPool 创建固定大小数量线程池,数量通过传入的参数决定。

  2. newSingleThreadExecutor 创建一个线程容量的线程池,所有的线程依次执行,相当于创建固定数量为 1 的线程池。

  3. newCachedThreadPool 创建可缓存的线程池,没有最大线程限制(实际上是 Integer.MAX_VALUE)。如果用空闲线程等待时间超过一分钟,就关闭该线程。

  4. newScheduledThreadPool 创建计划 (延迟) 任务线程池, 线程池中的线程可以让其在特定的延迟时间之后执行,也可以以固定的时间重复执行(周期性执行)。相当于以前的 Timer 类的使用。

  5. newSingleThreadScheduledExecutor 创建单线程池延迟任务,创建一个线程容量的计划任务。

Spring Boot中使用线程池

        如果使用 spring 框架的朋友,可以直接使用 spring 封装的线程池,由 spring 容器管理。Spring Boot中有两种方式配置线程池,一种是 自定义配置,二种是 修改原生 spring 异步线程池的装配。


上一篇: 什么是线程死锁?怎么避免?
下一篇: 多线程 start 和 run 方法到底有什么区别?
推荐文章
  • 在HTML中,如果你想让一个输入框(input元素)不可编辑,你可以通过设置其readonly属性来实现。示例如下:input type="text" value="此处内容不可编辑" readonly在上述代码中,readonly属性使得用户无法修改输入框中的内容。另外,如果你希望输入框完全不可交
  • ASP.NET教程ASP.NET又称为ASP+,基于.NETFramework的Web开发平台,是微软公司推出的新一代脚本语言。ASP.NET是一个使用HTML、CSS、JavaScript和服务器脚本创建网页和网站的开发框架。ASP.NET支持三种不一样的开发模式:WebPages(Web页面)、
  • C# 判断判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。下面是大多数编程语言中典型的判断结构的通常形式:判断语句C#提供了以下类型的判断语句。点击链接查看每个语句的细节。语句描述if语句一个 if语句 由一个布尔表达式后跟
  • C#循环有的时候,可能需要多次执行同一块代码。通常情况下,语句是顺序执行的:函数中的第一个语句先执行,接着是第二个语句,依此类推。编程语言提供了允许更为复杂的执行路径的多种控制结构。循环语句允许我们多次执行一个语句或语句组,下面是大多数编程语言中循环语句的通常形式:循环类型C#提供了以下几种循环类型
  • C#数组(Array)数组是一个存储相同类型元素的固定大小的顺序集合。数组是用来存储数据的集合,一般认为数组是一个同一类型变量的集合。声明数组变量并不是声明number0、number1、...、number99一个个单独的变量,而是声明一个就像numbers这样的变量,然后使用numbers[0]
  • ASP.NET是一个由微软公司开发的用于构建Web应用程序的框架,它是.NETFramework的一部分。它提供了一种模型-视图-控制器(MVC)架构、Web表单以及最新的ASP.NETCore中的RazorPages等多种开发模式,可以用来创建动态网页和Web服务。以下是一些基础的ASP.NET编
学习大纲