学习资源整理自:B站《狂神说》
书接上回
JUC并发编程
8、线程池(重点)
线程池:三大方法、七大参数、四种拒绝策略
池化技术
程序的执行,本质:占用系统的资源!优化资源的使用==》池化技术
线程池、连接池、内存池、对象池…
池化技术:事先准备好一些资源,有人要用,就来为这里拿,用完之后还给我。
线程池的好处
1、降低资源的消耗
2、提高响应的速度
3、方便管理
线程服用、可以管理最大并发数、管理线程
三大方法
newSingleThreadExecutor() 单线程线程池
private static void singlePool() {
ExecutorService threadPool_single = Executors.newSingleThreadExecutor();//单线程 线程池
//执行二十个任务
for (int i = 0; i < 20; i++) {
//使用线程池方式执行线程任务
threadPool_single.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 石似心");
});
}
//使用完之后需要关闭线程池
threadPool_single.shutdown();
}
控制台:
pool-1-thread-1 石似心
pool-1-thread-1 石似心
pool-1-thread-1 石似心
pool-1-thread-1 石似心
pool-1-thread-1 石似心
pool-1-thread-1 石似心
......
newFixedThreadExecutor() 固定大小线程池
public static void fixedPool(){
ExecutorService threadPool_fixed = Executors.newFixedThreadPool(5);//固定大小 线程池
//执行二十个任务
for (int i = 0; i < 20; i++) {
//使用线程池方式执行线程任务
threadPool_fixed.execute(()->{
System.out.println(Thread.currentThread().getName()+" 石似心");
});
}
//使用完之后需要关闭线程池
threadPool_fixed.shutdown();
}
控制台:
pool-1-thread-2 石似心
pool-1-thread-2 石似心
pool-1-thread-1 石似心
pool-1-thread-3 石似心
pool-1-thread-4 石似心
pool-1-thread-5 石似心
pool-1-thread-2 石似心
......
newCachedThreadExecutor() 弹性线程池
public static void cachedPool(){
ExecutorService threadPool_cached = Executors.newCachedThreadPool();//弹性线程池,遇强则强
//执行二十个任务
for (int i = 0; i < 20; i++) {
//使用线程池方式执行线程任务
threadPool_cached.execute(()->{
System.out.println(Thread.currentThread().getName()+" 石似心");
});
}
//使用完之后需要关闭线程池
threadPool_cached.shutdown();
}
控制台
pool-1-thread-8 石似心
pool-1-thread-6 石似心
pool-1-thread-7 石似心
pool-1-thread-4 石似心
pool-1-thread-3 石似心
pool-1-thread-4 石似心
pool-1-thread-10 石似心
......
七大参数
源码:
// 三大参数:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 21亿 OOM
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 实则调用 ThreadPoolExecutor
// 七大参数 :
public ThreadPoolExecutor(int corePoolSize, // 核心线程大小
int maximumPoolSize, //最大线程大小
long keepAliveTime, //线程存活时间,超时无人访问则会施放线程
TimeUnit unit, //超时单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程工厂,一般不用动
RejectedExecutionHandler handler //拒绝策略
) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
自定义线程池
阿里巴巴开发手册:
图解线程池:
代码实现:
public static void main(String[] args) {
// 创建线程池,使用 ThreadPoolExecutor
ExecutorService definedPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueu在这里插入图片描述
e<>(3), // 排队人数
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略
try {
for (int i = 0; i < 5; i++) {
definedPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
definedPool.shutdown();
}
}
四种拒绝策略
四个ThreadPoolExecutor的内部类,实现了RejectedExecutionHandler接口
线程最大承受数:最大线程数 + 阻塞队列数
达到最大承受数后:
-
new ThreadPoolExecutor.CallerRunsPolicy()
新来的任务会返回到创建线程池的线程执行,例如 main
-
new ThreadPoolExecutor.AbortPolicy()
新来的任务不会有线程处理,抛出异常
-
new ThreadPoolExecutor.DiscardPolicy()
新来的任务会被丢弃,不执行,不抛出异常
-
ThreadPoolExecutor.DiscardOldestPolicy()
新来的任务会尝试使用最早的运行的线程来运行,若最早的线程恰好执行完任务,则执行新的任务,若最早的任务亦没有处理完任务,则新来的任务会被抛弃,不执行,不抛出异常
CPU密集型、IO密集型(调优)
最大线程数如何定义呢?
1、CPU密集型:多少核就是多少线程,保持CPU的效率最高
2、IO密集型:判断程序中较为消耗IO的线程:例如15个大型任务,io十分消耗资源,则定义为 16个即可。
//获取CPU数
Runtime.getRuntime().availableProcessors();
9、四大函数式接口(必须掌握)
新时代程序员:
lamdba表达式、链式编程、函数式接口、Stream流试计算
所有函数式接口都可以用lamdba表达式简化
函数式接口:只有一个方法的接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
四个函数式接口
Function 函数式接口
源码:
// T传入参数类型,R返回值类型
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
代码实现:
public static void main(String args[]){
// Function 函数式接口
// 工具类:将Integer转为String
Function<Integer,String> function = new Function<Integer,String>(){
@Override
public String apply(Integer i) {
return String.valueOf(i);
}
};
System.out.println(function.apply(123));
}
Predicate 断定型接口
源码:
//T传入参数类型,返回boolean
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
代码实现:
public static void main(String args[]){
// 工具类:判断字符串是否等于 “周杰伦”
// 使用lamdba简化
Predicate<String> predicate = (str)->{return "周杰伦".equals(str);};
//使用 predicate
System.out.println(predicate.test("石似心"));
//consle:false
}
Consumer 消费型接口
代码实现:
public static void main(String[] args) {
//消费型接口 只有参数,没有返回值
Consumer<String> consumer = (str)->{System.out.println("打印:"+str);};
consumer.accept("石似心");
}
Supplier 供给型接口
代码实现:
public static void main(String args[]){
// Supplier 供给型接口
Supplier<String> supplier = new Supplier<String>() {
@Override
public String get() {
return "1024";
}
};
System.out.println(supplier.get());
}
10、Stream 流式计算
存储 + 计算
题目要求:一行代码实现
现有5个用户,筛选:
1、ID必须是偶数
2、年龄大于23岁
3、用户名转为大写
4、用户名倒序
5、只输出第一个用户
public static void main(String args[]){
/**
题目要求:一行代码实现
现有5个用户,筛选:
1、ID必须是偶数
2、年龄大于23岁
3、用户名转为大写
4、用户名倒序
5、只输出第一个用户
*/
User user1 = new User(1,22,"a");
User user2 = new User(2,23,"b");
User user3 = new User(3,24,"c");
User user4 = new User(4,25,"d");
User user5 = new User(6,26,"e");
// lamdba、链式编程、函数式接口、stream流式计算
List<User> users = Arrays.asList(user1, user2, user3, user4, user5);
users.stream()
.filter(u->{return (u.getId()%2==0)&&(u.getAge()>23);})
.map((u)->{u.setName(u.getName().toUpperCase());return u;})
.sorted((u1,u2)->{return u2.getName().compareTo(u1.getName());})
.limit(1)
.forEach(System.out::println);
}
11、ForkJoin
概念:拆分合并
从JDK1.7开始,Java提供ForkJoin框架用于并行执行任务,它的思想就是讲一个大任务拆分成若干小任务,最终合并每个小任务的结果得到这个大任务的结果。
我的个人理解;是一个大型递归拆分任务,并且有专门线程池执行细小任务,然后逐层合并向上返回结果的框架。
ForkJoin 特点:工作窃取
当B线程率先完成了B任务队列的任务,就会去A线程中,窃取一个任务过来执行。
官方文档:
ForkJoinPool
ForkJoinTask:计算类需要继承分之合并任务类
RecursiveAction:递归事件类,没有返回值,只顾执行
RecursiveTask:递归任务类,有返回值
代码实现:
计算:从1加到10亿
任务类:
import java.util.concurrent.RecursiveTask;
/**
* 任务类,继承 RecursiveTask
* 将大任务 拆分!!!
* @author: stone
* @create: 2020-08-23 22:48
*/
public class DemoTask extends RecursiveTask<Long> {
private long start;// 0
private long end;// 1_0000_0000
private long flag = 1000l;// 拆分点:每1000拆分出一个任务
public DemoTask(long start, long end) {
this.start = start;
this.end = end;
}
/**
* 实现RecursiveTask抽象方法
* 执行具体业务的方法
* @return
*/
@Override
protected Long compute() {
Long resultLong=0l;
//规划两条路线,当值在 1~1000内 时执行循环“加”
if((end-start)<flag){
//当前传进来的start到end数值距离小于1000
//属于小任务
for (long i = start; i <= end; i++) {
resultLong += i;
}
}else{
//任务还没达到拆分点,需要拆分
//1、当前两个值去中间值 将任务对半分
long middle = (start+end)/2;
//2、创建任务类
DemoTask demoTask1 = new DemoTask(start,middle);
DemoTask demoTask2 = new DemoTask(middle+1, end);
//3、将任务执行:使用fork() 将任务压入线程队列
demoTask1.fork();
demoTask2.fork();
//4、获取子任务的执行结果,向上返回
resultLong = demoTask1.join() + demoTask2.join();
}
return resultLong;
}
}
执行任务类:
/**
* 使用forkJoin
* @throws ExecutionException
* @throws InterruptedException
*/
static void userForkJoin() throws ExecutionException, InterruptedException {
ForkJoinTask<Long> demoTask = new DemoTask(0l, 10_0000_0000l);
//1、使用 ForkJoinPool执行 ForkJoinTask,
// ForkJoinPool执行:execute()只执行;submit()有返回值;invoke()貌似使用于集合;
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> result = forkJoinPool.submit(demoTask);
System.out.println(result.get());// 阻塞主线程等待结果
// console : 500000000500000000
}
拓展:使用strem流计算从0加到10亿
JDK1.8的stream流并行计算,速度快十倍…
/**
* 使用Stream并行流计算
*/
static void userStream(){
long reduce = LongStream.rangeClosed(0l, 10_0000_0000l).parallel().reduce(0, Long::sum);
System.out.println("结果:"+reduce);
// console: 结果:500000000500000000
}
未完待续