线程池
传统线程的缺点:
1.每次都需要创建和消耗线程,是需要消耗系统资源的
2.线程没有任务管理的功能,当任务量比较大的时候没有任务队列对任务进行管理或者是拒绝任务
1.线程池
1.1定义:
线程池就是使用池化技术管理线程和使用线程的一种机制
1.2线程池的优点
1.利用线程池可以复用线程,控制最大并发数(避免了频繁创建和消耗线程池所带来的性能消耗)
2.实现任务缓存和任务拒绝机制
3.实现延迟任务执行
1.3线程池的使用
两种实现方式
1.通过ThreadPoolExecutor手动创建线程(1种实现方法)
2.通过Executors执行器自动创建线程池(6种实现方法)
七种实现方法:
线程池创建方法1 :创建一个固定大小的线程池
//创建一个包含5个线程的线程池
ExecutorService threadPool=Executors.newFixedThreadPool(5);
使用线程池执行任务
1.submit()
2.execut()
//使用submit()
//使用线程池执行任务
for (int i = 0; i < 5; i++) {
//给线程池添加任务(使用线程池执行任务)
threadPool.submit(new Runnable() {
@Override
public void run() {
System.out.println("线程名称"+Thread.currentThread().getName());
}
});
}
//使用execute()
//使用线程池执行任务
for (int i = 0; i < 5; i++) {
//给线程池添加任务(使用线程池执行任务)
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("线程名称"+Thread.currentThread().getName());
}
});
}
使用submit它可以执行有返回值的任务或者是无返回值的内容;而execute只能执行不带返回值的任务
线程池中线程工厂的作用:
为线程池提供线程的创建。
提供的功能:
1.设置(线程池中的)线程的命名规则
2.设置线程的优先级
3.线程线程分组
4.设置线程类型(用户线程&守护线程)
线程池创建方法2:带缓存的线程池
线程池会根据任务数创建线程池,并且在一定时间内可以重复使用这些线程
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池用法2:带缓存的线程池
*/
public class ThreadPoolDemo2 {
public static void main(String[] args) {
//创建线程池
ExecutorService service=Executors.newCachedThreadPool();
for (int i = 0; i < 1000; i++) {
int finalI = i;
service.submit(()->{
System.out.println("i"+ finalI +"线程名称"+Thread.currentThread().getName());
});
}
}
}
优点:更多任务数量产生相应的线程池。
缺点:占用资源数量比较多。
适用于短时间内有大量任务的场景。
线程池用法3:执行定时任务的线程池
1.只执行一次的定时任务
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
*执行定时任务的线程池
*/
public class ThreadPoolDemo3 {
public static void main(String[] args) {
//创建线程池
ScheduledExecutorService service=Executors.newScheduledThreadPool(5);
System.out.println("添加任务时间:"+LocalDateTime.now());
//执行定时任务(延迟3s执行)
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行了任务:"+LocalDateTime.now());
}
},3,TimeUnit.SECONDS);
}
}
2.固定频率执行``
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
*执行定时任务的线程池
*/
public class ThreadPoolDemo3 {
public static void main(String[] args) {
//创建线程池
ScheduledExecutorService service=Executors.newScheduledThreadPool(5);
System.out.println("添加任务时间:"+LocalDateTime.now());
// scheduleTest(service);//只执行一次的定时任务
//2s之后开始执行定时任务,定时任务每隔4s执行一次
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+LocalDateTime.now());
}
},2,4,TimeUnit.SECONDS);
}
/**
* 执行一次的定时任务
* @param service
*/
private static void scheduleTest(ScheduledExecutorService service) {
//执行定时任务(延迟3s执行)
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行了任务:"+LocalDateTime.now());
}
},3,TimeUnit.SECONDS);
}
}
注意事项:
在 scheduleAtFixedRate 中,有时候任务执行时间大于延迟任务设定的间隔时间,那么当任务执行完之后才会开始执行下次任务,此时并不是以设定的间隔时间来执行任务的。比如定时任务的间隔为 3s,任务执行需要花费 4s,那么每次执行实际的时间间隔为 4s。(那个大,听那个),如下代码所示:
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("执行时间:"+LocalDateTime.now());
try {
Thread.sleep(5*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,4,TimeUnit.SECONDS);
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
*执行定时任务的线程池
*/
public class ThreadPoolDemo3 {
public static void main(String[] args) {
//创建线程池
ScheduledExecutorService service=Executors.newScheduledThreadPool(5);
System.out.println("添加任务时间:"+LocalDateTime.now());
// scheduleTest(service);//只执行一次的定时任务
//2s之后开始执行定时任务,定时任务每隔4s执行一次
// service.scheduleAtFixedRate(new Runnable() {
// @Override
// public void run() {
// System.out.println("执行任务:"+LocalDateTime.now());
// }
// },2,4,TimeUnit.SECONDS);
//2s之后开始执行定时任务,定时任务每隔4s执行一次
service.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
System.out.println("执行时间:"+LocalDateTime.now());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},2,4,TimeUnit.SECONDS);
}
/**
* 执行一次的定时任务
* @param service
*/
private static void scheduleTest(ScheduledExecutorService service) {
//执行定时任务(延迟3s执行)
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行了任务:"+LocalDateTime.now());
}
},3,TimeUnit.SECONDS);
}
}
线程池创建方法4:单线程执行定时任务的线程池(newScheduledThreadPool的单线程版本)
import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo4 {
public static void main(String[] args) {
//创建执行定时任务的单线程线程池
ScheduledExecutorService service= Executors.newSingleThreadScheduledExecutor();
service.schedule(new Runnable() {
@Override
public void run() {
System.out.println("执行任务:"+ LocalDateTime.now());
}
},2, TimeUnit.SECONDS);
}
}
线程池创建方法5:单线程线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池创建方法5:单线程线程池
*/
public class ThreadPoolDemo5 {
public static void main(String[] args) {
ExecutorService service=Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
service.submit(new Runnable() {
@Override
public void run() {
System.out.println("线程名:"+Thread.currentThread().getName());
}
});
}
}
}
面试题:单线程的线程池有什么意义?
1.复用线程
2.单线程的线程池提供了任务队列和拒绝策略的
(拒绝策略)当任务队列满了之后(Integer.MAX_VALUE),新来的任务就会回字形拒绝策略的
线程池创建方法6:根据当前CPU生成线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo6 {
public static void main(String[] args) {
// 根据当前设备自动生成线程池
ExecutorService service = Executors.newWorkStealingPool();
for (int i = 0; i < 100; i++) {
service.submit(() -> {
System.out.println("线程名:" + Thread.currentThread().getName());
});
}
while (!service.isTerminated()) {
}
}
}
2.ThreadPoolExecutor
2.1 Executors 自动创建线程池可能存在的问题
2.1.1 OOM 代码演示
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 演示OOM
*/
public class ThreadPoolDemo7 {
static class OOMClass{
// 1M 空间(M KB Byte)
private byte[] bytes=new byte[1*1024*1024];
}
public static void main(String[] args) {
ExecutorService service=Executors.newCachedThreadPool();
OOMClass[] objects=new OOMClass[15];
for (int i = 0; i < 15; i++) {
int finalI = i;
service.execute(()->{
try {
Thread.sleep(finalI * 200);
} catch (InterruptedException e) {
e.printStackTrace();
}
OOMClass oomClass=new OOMClass();
objects[finalI]=oomClass;
System.out.println("执行第"+finalI+"次");
});
}
}
}
2.1.2 关于参数设置
XX:标准设置,所有 HotSpot 都支持的参数。
-X:非标准设置,特定的 HotSpot 才支持的参数。
-D:程序参数设置,-D参数=value,程序中使用:System.getProperty(“获取”)。
线程池创建方法7:手动方式创建线程池
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 线程池创建方法7:手动方式创建线程池
*/
public class ThreadPoolDemo10 {
public static void main(String[] args) {
ThreadFactory factory=new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread=new Thread(r);
return thread;
}
};
//手动方法创建线程池
ThreadPoolExecutor executor=
new ThreadPoolExecutor(5,10,10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100),factory,
new ThreadPoolExecutor.DiscardPolicy());
for (int i = 0; i < 10; i++) {
executor.submit(()->{
System.out.println("线程名称"+Thread.currentThread().getName());
});
}
}
}
线程池执行流程
线程池的重要执行节点
1.当任务来了之后,判断线程池中实际线程数是否小于核心数线程,如果小于就直接创建线程并执行任务。
2.当实际线程数大于核心线程数(正式员工),他会判断任务队列是否已满,如果未满直接将任务存放队列即可。
3.判断线程池的实际线程数是否大于最大线程数(正式员工+临时员工),如果小于最大线程数直接创建线程执行任务;实际线程数已经等于了最大线程数了,那么会直接执行拒绝策略。
线程池拒绝策略:
(4[JDK提供的拒绝策略]+1[自定义拒绝策略])
自定义拒绝策略:
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor
executor) {
// 执行自定义拒绝策略的相关操作
System.out.println("我是自定义拒绝策略~");
}
});