线程池
线程池是什么
线程池就相当于把资源提前准备好,可以随时在池子中来取,降低了资源消耗
提前把线程创建好,放到一个池子里,后续在申请线程,直接冲池子里取,用完了之后还回到池子中,比向操作系统申请更快
标准库中的线程池
ThreadPoolExecutor
创建这个类的实例的时候,要填写一系列参数
ThreadPoolExecutor 提供了更多的可选参数,可以进⼀步细化线程池⾏为的设定.
java 的线程池,把这里的线程分成两种
1.核心线程
2.临时线程
Java的线程 创建的时候,会申请一些线程(一创建就申请的线程就是核心线程),会把一些任务丢给这些线程来完成
如果核心线程处理不过来了,此时线程池就会再从系统申请一些其他的线程来帮忙(后来帮忙的线程就是临时线程)
等到不忙了,临时线程就会被释放掉,核心线程不会释放(除非整个线程销毁)
ThreadPoolExecutor 提供了更多的可选参数,可以进⼀步细化线程池⾏为的设定
• corePoolSize【核心线程数】: 正式员⼯的数量.(正式员⼯,⼀旦录⽤,永不辞退)
• maximumPoolSize【总数(最大线程数) {包括核心和临时}】:正式员⼯+临时⼯的数⽬.(临时⼯:⼀段时间不⼲活,就被辞退).
• long keepAliveTime【存活时间】:临时⼯允许的空闲时间.时间单位(秒,分钟,毫秒)
• unit:keepaliveTime 的时间单位,是秒,分钟,还是其他值.
• BlockingQueue< Runnable > workQueue:传递任务的阻塞队列(线程池要完成任务的队列),每个任务都是通过Runnable 来描述的(线程池在使用的时候,首先会创建一些线程,然后需要调用者给线程池里放一些“任务” 这些线程就会从队列中取出任务并执行里面的代码)
• ThreadFactory threadFactory【线程工厂】: 创建线程的⼯⼚,参与具体的创建线程⼯作.通过不同线程⼯⼚创建出的线程相当于对⼀些属性进⾏了不同的初始化设置.
threadfactory 就是 Thread 类的工厂类
由于在线程池中是需要创建很多线程的,创建线程需要的初始化操作就可以通过 threadFactory 来进行设定(如通过newThread() 给线程设置名字,或者前台或后台线程)
• RejectedExecutionHandler【拒绝策略】: 如果任务量超出公司的负荷了接下来怎么处理
(比如线程池有任务队列,有阻塞队列,当任务队列满了的时候,如果再次添加新任务,正常来说,除非特殊说明,我们写的代码是不希望会出现这种突发性的阻塞的(可能会对程序造成不可预估的影响),因此直接添加线程阻塞是不太好的,因此标准库引入了 “拒绝策略”)
Java标准库就为此提供了四种拒绝策略
标准库提供了一个简化的版本——Executors
Executors本质上是对ThreadPoolExecutor 进行了封装,本质上也是一个工厂类,提供了一些工厂方法,对上述的 ThreadPoolExecutor 进行不同的初始化
上面的工厂方法,就是在创建不同风格的线程池
使用示例:
实现线程池
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class MyFixedThreadPool{
BlockingQueue<Runnable> queue=new LinkedBlockingQueue<>();
public MyFixedThreadPool(int n){//处理任务
//创建n个线程
for(int i=0;i<n;i++){
Thread t=new Thread(()->{
//执行任务
while (true){
try {
Runnable s=queue.take();
s.run();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
public void submit(Runnable task) throws InterruptedException {//接收任务
queue.put(task);
}
}
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
MyFixedThreadPool t=new MyFixedThreadPool(4);
for(int i=0;i<100;i++){
int id=i;
t.submit(()->{
System.out.println("执行任务:"+id);
});
}
}
}
定时器
定时器是什么
定时器类似于一个闹钟,达到一个设定的时间后,就执行某个指定好的代码
标准库中的定时器
Java标准库提供了Timer 类,其核心方法为 schedule
Timer timer=new Timer();
timer.schedule(null,0);
schedule 中有两个参数,第一个参数指定将要执行的代码,第二个参数指定经过多长时间执行
应用:
实现定时器
实现一个定时器MyTimer
import java.util.PriorityQueue;
//自己实现定时器
//要能够同时管理多个任务
//有一定的数据结构来阻止这多个任务
//用优先级队列储存任务(按照时间先后顺序执行)
class MyTimerTask implements Comparable<MyTimerTask>{
private Runnable task;
private long time;
public MyTimerTask(Runnable runnable,long time){
this.task=runnable;
this.time=System.currentTimeMillis()+time;//保存具体使用的时间戳
}
public Runnable getTask(){
return this.task;
}
public long getTime(){
return this.time;
}
@Override//按时间先后比较放入
public int compareTo(MyTimerTask o) {
return (int) (this.time-o.time);
}
}
class MyTimer{
private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();
public MyTimer(){
Thread t=new Thread(()->{//添加线程,让判断是否出列独立执行,让其他线程能继续执行下去
while (true){//让线程一直执行下去,保证后续如果加了任务还会运行
if (queue.isEmpty()){
continue;
}
MyTimerTask task=queue.peek();
if (System.currentTimeMillis()<task.getTime()){
continue;
}
task.getTask().run();
queue.poll();
}
});
t.start();
}
public void schedule(Runnable runnable,long delay){//设定最大等待时间
queue.offer(new MyTimerTask(runnable,delay));
}
}
执行结果:
但是上述代码存在几个问题
1.线程安全:两个线程对同一个queue 进行出和入队列操作
解决方法:对独立额操作的部分加锁
2.出现忙等,继而引起线程饿死:
解决方法:用wait、notify
修正结果:
import java.util.PriorityQueue;
//自己实现定时器
//要能够同时管理多个任务
//有一定的数据结构来阻止这多个任务
//用优先级队列储存任务(按照时间先后顺序执行)
class MyTimerTask implements Comparable<MyTimerTask>{
private Runnable task;
private long time;
public MyTimerTask(Runnable runnable,long time){
this.task=runnable;
this.time=System.currentTimeMillis()+time;//保存具体使用的时间戳
}
public Runnable getTask(){
return this.task;
}
public long getTime(){
return this.time;
}
@Override//按时间先后比较放入
public int compareTo(MyTimerTask o) {
return (int) (this.time-o.time);
}
}
class MyTimer{
private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();
private Object locker=new Object();//引入锁
public MyTimer(){
Thread t=new Thread(()->{//添加线程,让判断是否出列独立执行,让其他线程能继续执行下去
while (true){//让线程一直执行下去,保证后续如果加了任务还会运行
try {
synchronized (locker){
if (queue.isEmpty()){
locker.wait();
}
MyTimerTask task=queue.peek();
if (System.currentTimeMillis()<task.getTime()){
locker.wait(task.getTime()-System.currentTimeMillis());
//此处有两个情况:
//1.随时有新任务,随时就要唤醒(新任务可能是时间靠前的任务)
//2.如果没有新任务,等到一定时间就自然唤醒
}else {
task.getTask().run();
queue.poll();
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
t.start();
}
public void schedule(Runnable runnable,long delay){//设定最大等待时间
synchronized (locker){
queue.offer(new MyTimerTask(runnable,delay));
locker.notify();//唤醒锁
}
}
}