四大经典案例_线程池及java代码实现
大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流
作者简介:
- CSDN java领域新星创作者blog.csdn.net/bug…
- 掘金LV3用户 juejin.cn/user/bug…
- 阿里云社区专家博主,星级博主,developer.aliyun.com/bug…
- 华为云云享专家 bbs.huaweicloud.com/bug…
线程池
我们之前学过常量池!这里的线程池也大同小异!
我们通过创建很多个线程放在一块空间不进行销毁,等到需要的时候就启动线程!避免了创建销毁的时间开销! 提高开发效率!
我们之前不是说一个线程创建并不会划分很多时间吗! 但是我们的多线程编程,有时候需要使用到很多很多线程,如果要进行创建,效率就不高,而线程池或者协程(我们后面会介绍)就避免了创建销毁线程! 但我们需要用到线程时,自己从线程池中给出就好!
我们创建线程的本质还是要通过内核态(就是我们的操作系统)进行创建,然而内核态创建的时间,我们程序员无法掌控,而通过线程池,我们就可以避免了内核态的操作,直接在用户态,进行线程的调用,也就是应用程序层!
使用线程池大大提高了我们的开发效率!
我们来学习一下java
中给我们提供的线程池类,然后自己实现一个线程池!
ThreadPoolExecutor
线程池
这个类在java.util.concurrent
并发编程包下,我们用到的很多关于并发编程的类都在!
可以看到这个线程池有4个构造方法!
我们了解一下参数最多的那个方法!
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
创建一个新的 ThreadPoolExecutor与给定的初始参数。
参数
corePoolSize - 即使空闲时仍保留在池中的线程数,除非设置 allowCoreThreadTimeOut
maximumPoolSize - 池中允许的最大线程数
keepAliveTime - 当线程数大于内核时,这是多余的空闲线程在终止前等待新任务的最大时间。
unit - keepAliveTime参数的时间单位
workQueue - 用于在执行任务之前使用的队列。 这个队列将仅保存execute方法提交的Runnable任务。
threadFactory - 执行程序创建新线程时使用的工厂
handler - 执行被阻止时使用的处理程序,因为达到线程限制和队列容量
我们这里的线程池类比一个公司,便于我们理解该类
int maximumPoolSize,
核心线程数(正式员工)maximumPoolSize
池中允许的最大线程数(正式员工+临时工)long keepAliveTime,
多余的空闲线程的允许等待的最大时间(临时工摸鱼时间)TimeUnit unit,
时间单位
-BlockingQueue<Runnable> workQueue,
任务队列,该类中用一个submit
方法,用于将任务注册到线程池,加入到任务队列中!ThreadFactory threadFactory,
线程工厂,线程是如何创建的RejectedExecutionHandler handler
拒绝策略
但任务队列满了后怎么做
1.阻塞等待,
2.丢弃久任务
3.忽略新任务
…
可以看到java
给我们提供的这个线程池类让人头大!
但是不必焦虑,我们只需要知道int maximumPoolSize,
核心线程数和 maximumPoolSize
池中允许的最大线程数即可!
面试问题
思考一个问题
我们有一个程序需要多线程并发处理一些任务,使用线程池的话,需要设置多大的线程数?
这里的话,我们无法准确的给出一个数值,我们要通过性能测试的方式找个一个平衡点!
例如我们写一个服务器程序:服务器通过线程池多线程处理机用户请求!如果要确定线程池的线程数的话,就需要通过对该服务器进行性能分析,构造很多很多请求模拟真实环境,根据这里不同的线程数,来观察处理任务的速度和当个线程的
cpu
占用率!从而找到一个平衡点!
如果cpu
暂用率过高,就无法应对一些突发情况,服务器容易挂!
我们java
根据上面的ThreadPoolExecutor
类进行封装提供了一个简化版本的线程池!Executors
供我们使用!
我们通过Executors
的使用学习,实现一个线程池!
Executors
java.util.concurrent.Executors
下面都是Executor
类中创建线程池的一些静态方法
创建可以扩容的线程池
创建一个指定容量的线程池
创建单线程池
创建一个线程池含有任务队列
我们重点学习创建指定大小得到线程池方法!
//Executors使用案例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo3 {
public static void main(String[] args) {
//创建一个指定线程个数为10的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
int finalI = i;
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello executor!"+ finalI);
}
});
}
}
}
我们通过ExecutorService
类中的submit
可以将多个任务注册到线程池中,然后线程池中的线程将任务并发执行,大大提升了编程效率!可以看到,啪的一下,100个任务给10个线程一下就执行结束了!
实现线程池
我们还是分析一下线程池用什么功能,里面都有些啥!
- 能够描述任务(直接用
runnable
) - 需要组织任务(使用
BlockingQueue
) - 能够描述工作线程
- 组织线程
- 需要实现往线程池里添加任务
//模拟实现线程池
class ThreadPool {
//描述任务 直接使用Runnable
//组织任务
private BlockingQueue<Runnable> queue = new LinkedBlockingDeque<>();
//描述工作线程
static class Worker extends Thread {//继承Thread类
BlockingQueue<Runnable> queue = null;
@Override
public void run() {
while (true){
try {
//拿到任务
Runnable runnable = queue.take();
//执行任务
runnable.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//通过构造方法拿到外面的任务队列!
public Worker(BlockingQueue<Runnable> queue) {
this.queue = queue;
}
}
//组织多个工作线程
//将多个工作线程放入到workers中!
public List<Thread>workers = new LinkedList<>();
public ThreadPool(int n) {//指定放入线程数量
for (int i = 0; i < n; i++) {//创建多个工作线程
Worker worker = new Worker(queue);
worker.start();//启动工作线程
workers.add(worker);//放入线程池
}
}
//创建一个方法供我们放入任务
public void submit(Runnable runnable){
try {
queue.put(runnable);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//测试代码
public class demo5 {
public static void main(String[] args) {
//线程池线程数量10
ThreadPool pool = new ThreadPool(10);
for (int i = 0; i <100 ; i++) {//100个任务
int finalI = i;
pool.submit(new Runnable() {
@Override
public void run() {
System.out.println("hello ThreadPool "+ finalI);
}
});
}
}
}
运行效果
案例总结
- 线程安全单例模式
- 阻塞队列->生产着消费者模型
- 定时器
- MyTask类描述一个任务 Runnable + time
- 带有优先级的阻塞队列
- 扫描线程,不停从队首取出元素,检测时间是否到达,并且执行任务,使用
wait
解决忙等位问题! - 实现
schedule
方法
- 线程池
- 描述一个任务
Runnable
- 组织任务,带有优先级的阻塞队列
- 创建一个工作线程
work类
,从任务队列获取任务,执行任务 - 组织工作线程
works
数据结构存放work
- 实现一个
submit
方法将任务放入任务队列中!
- 点赞
- 收藏
- 关注作者
评论(0)