在项目里面为了提高性能往往会在主线程里面开启一个新线程去执行,这种做法最方便快捷,但是当用户量数据上涨,很显然每次去开启新的线程服务器往往会吃不消,这时就需要线程池来管理和监控线程的状态。
创建多线程的三种方式
java 多线程很常见,如何使用多线程,如何创建线程,java 中有三种方式:
通过继承Thread接口public class Mytheard1 extends Thread {
@Override
public void run() {
for (int i = 0; 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
System.out.println("thread#2===" + i);
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
通过实现Callable接口public class Mytheard3 implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; 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 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(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 提供了几种常用的线程池,可以快捷的供程序员使用newFixedThreadPool创建固定大小数量线程池,数量通过传入的参数决定。
newSingleThreadExecutor创建一个线程容量的线程池,所有的线程依次执行,相当于创建固定数量为 1 的线程池。
newCachedThreadPool创建可缓存的线程池,没有最大线程限制(实际上是 Integer.MAX_VALUE)。如果用空闲线程等待时间超过一分钟,就关闭该线程。
newScheduledThreadPool 创建计划 (延迟) 任务线程池, 线程池中的线程可以让其在特定的延迟时间之后执行,也可以以固定的时间重复执行(周期性执行)。相当于以前的 Timer 类的使用。
newSingleThreadScheduledExecutor创建单线程池延迟任务,创建一个线程容量的计划任务。
Spring Boot中使用线程池
如果使用 spring 框架的朋友,可以直接使用 spring 封装的线程池,由 spring 容器管理。Spring Boot中有两种方式配置线程池,一种是 自定义配置,二种是 修改原生 spring 异步线程池的装配。
YUKX优科学习网
本文内容及图片来源于网上摘录或读者投稿