【总结】线程池

线程池

01 / ThreadPoolExecutor

1.1实现机制

image-20210917154319231

线程每次new都要和操作系统申请资源,且用完后就烧毁了,没有复用性

处理任务四大步:

1、当前已有的线程数有没有达到核心的线程数

2、已经达到了核心线程数,将任务放到队列里面去

3、当前并发太大,队列满了,再提交任务,判断最大线程数有没有达到,没有就创建核心线程之外的线程

4、达到最大线程数后,满负荷时,拒绝任务

拒绝有四个策略:

1、谁提交的任务,谁自己去执行(主线程)

2、直接抛异常

3、放弃任务,不管了

4、还要执行新执行的任务,放弃队头的任务(放入队列时间最长的任务)

由核心线程–>队列—>最大线程的扩展顺序

1.2核心参数

image-20210917171306363

1.3生命周期

image-20210917171743086

1.线程池的五种状态,只能由小到大迁移,即-1>0>1>2>3。

2.shutdown(不清空任务队列、 会等它们完成,shutdownNow)会清 空任务队列、不等它们完成。shutdown()只中断空闲的线程,shutdownNow()会 中断所有的线程,不管你执行没执行完

3.TIDYING 和TREMINATED二者之间执行了一个钩子函数terminated(,目前这是-个空的实现。

1.4阅读源码

实现机制: execute()
核心参数:实例变量
生命周期:静态常量
拒绝策略: CallerRunsPolicy, AbortPolicy, DiscardPolicy, DiscardOldestPolicy
提交方法: execute(), submit(), newTaskFor(), FutureTask

execute:

//没达到核心线程数,就创建线程,达到了就加入到队列里,加入队列失败,再次创建线程(非核心线程),如果还是失败,就拒绝

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
       
        int c = ctl.get();//获取线程状态
        if (workerCountOf(c) < corePoolSize) {//线程数量是否小于核心线程数
            if (addWorker(command, true))//加到线程里,核心线程的身份
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {//已到核心线程数,线程池还在运行,将任务加到队列里
            int recheck = ctl.get();//检验
            if (! isRunning(recheck) && remove(command))//如果不是运行状态就一处
                reject(command);//拒绝
            else if (workerCountOf(recheck) == 0)//核心线程数等于0
                addWorker(null, false);//创建一个线程
        }
        else if (!addWorker(command, false))
            reject(command);
    }

addWorker:添加一个线程

添加一个任务,看是不是核心任务

retry:外层循环的名字

break retry:跳出循环

//创建一个线程,将其加入线程池,加入成功就运行
for (;;) {
                int wc = workerCountOf(c);
    //获取工作线程的数量,超过(核心或者普通线程数)限制,返回false
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
//workers是一个容器就是一个线程池的体现,线程加入成功就运行

Worker:封装线程和任务

线程池的内部类继承AQS实现了Runnable接口

 private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable

线程池里面的线程复用

不管是核心线程还是非核心线程,空闲下来后都会去队列里面拿任务执行。没有任务就还是空闲,超过一定时间就会被释放。

线程池里面一定要有一定数量的线程,保证任务到来时,能够处理

execute是没有返回值的,调用的任务也是要没有返回值的。那怎么线程池调用一个有返回值的任务呢?

submit

适配器模式:把A接口变成B接口,为什么要把A接口变成B接口?

例如:我有一个狗和人的接口,程序中想调用人的玩方法,但是只能传入狗。这个时候就需要将狗是配成人。继承狗的实现类实现人接口的方法。(继承原实现目标)

image-20210917182749535

//将Callable适配成Runnable
public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);//将task转为Runnable
        execute(ftask);//利用execute执行
        return ftask;
    }

submit传进了Callable,将Callable转为Runnable再调用 execute

class FutureTask<V> implements RunnableFuture<V> //继承Runnable接口的子接口,需要重写run方法
    
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

02 / ScheduledThreadPoolExecutor

2.1核心功能

image-20210918144953129

2.2阅读源码

1.延迟执行任务:
DelayedWorkQueue, ScheduledThreadPoolExecutor(). schedule()
2.周期执行任务:
scheduleAtFixedRate(), scheduleWithFixedDelay(). ScheduledFutureTask

怎么实现的延迟?

在出队的时候加以判断,如果时间到了就正常出队,要是时间没到,就阻塞

static class DelayedWorkQueue extends AbstractQueue<Runnable>
        implements BlockingQueue<Runnable> {
    
 long delay = first.getDelay(NANOSECONDS);
                        if (delay <= 0)
                            return finishPoll(first);
                        first = null; // don't retain ref while waiting
                        if (leader != null)
                            available.await();

构造器:

public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory,
                                       RejectedExecutionHandler handler) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler);
    }

核心线程数,,,,队列,对于这个线程池队列必须是延迟队列,利用延迟队列来实现延迟周期性的动作

延迟执行逻辑:

将延迟和任务封装打包,将打包的数据加入队列,队列是延迟的,一定是到时 间了才执行

RunnableScheduledFuture<?> t = decorateTask(command,
            new ScheduledFutureTask<Void>(command, null,
//将任务和延迟时间封装成对象,将封装之后的任务,将其加入队列             

周期执行逻辑:

将任务和时间封装之后的任务加入延迟队列

scheduleAtFixedRate:

ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        

scheduleWithFixedDelay:

ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(-delay));
       

两者唯一的区别就是:对周期的处理不一样,一正一负

先去检查状态再去检查类型,再决定使用什么样的run方法(不是周期性的,那就调父类的run方法,不加周期的处理)

03 / Executors

Executors采用无界队列,容易造成OOM,不建议使用。

image-20210918151707978

并发工具

01 / Semaphore

Semaphore就是信号量,提供了资源数量的并发访问控制。假设初始化Semaphore时指定的资源数量是10,那么若有N个线程来获取Semaphore里面的资源,N个线程中只有10个线程能获取到,其他线程都会阻塞。直到有线程释放了资源,其他线程才能获取到。
当初始资源的个数为1的时候,Semaphore退化为 排他锁。Semaphone的实现原 理和锁十分类似,也是基于AQS,也有公平和非公平之分。

Sync是AQS的子类继承AQS,它就是一个同步器

02 / CountDownLatch

主线程要等待N个Worker线程工作完毕才退出,就可以使用CountDownLatch来实现。
CountDownL atch的原理和Semaphore原理类似,同样是基于AQS,不过没有公平和非公平之分。

03 / CyclicBarrier

CountDownLatch的计数器是一次性的, 也就是等到计数器值变为0后,再调用CountDownLatch的await()和countDown()方法都会立刻返回,此时的CountDownLatch就起不到线程同步 的效果了。
为了满足计数器可以重置的需要,JDK开 发组提供了CyclicBarrier类,并 且CyclicBarrier类的功能并不限于CountDownL atch的功能。从字面意思理解,CyclicBarrier是回环屏障的意思, 它可以让一组线程全部达到一个状态后再全部同时执行。
之所以叫作回环是因为当所有等待线程执行完毕,并重置CyclicBarrier的状态后它可以被重用。
变为0后,再调用CountDownLatch的await()和countDown()方法都会立刻返回,此时的CountDownLatch就起不到线程同步 的效果了。
为了满足计数器可以重置的需要,JDK开 发组提供了CyclicBarrier类,并 且CyclicBarrier类的功能并不限于CountDownL atch的功能。从字面意思理解,CyclicBarrier是回环屏障的意思, 它可以让一组线程全部达到一个状态后再全部同时执行。
之所以叫作回环是因为当所有等待线程执行完毕,并重置CyclicBarrier的状态后它可以被重用。
之所以叫作屏障是因为线程调用await()方法后就会被阻塞,这个阻塞点就称为屏障点,等所有线程都调用了await()方法后,线程就会突破屏障,继续执行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值