Java - ThreadPoolExecutor

[构造方法参数说明]

corePoolSize: 核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。
maximumPoolSize: 线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。
keepAliveTime: 非核心线程的闲置超时时间,超过这个时间就会被回收。
unit: 指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。
workQueue: 线程池中的任务队列.
常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。
threadFactory: 线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法

[原理]

  1. 如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。
  2. 如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
  3. 如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。
  4. 如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
  5. 线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。

[Executor拒绝策略]

  1. AbortPolicy: 为java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try
    catch,否则程序会直接退出.
  2. DiscardPolicy: 直接抛弃,任务不执行,空方法
  3. DiscardOldestPolicy: 从队列里面抛弃head的一个任务,并再次execute 此task。
  4. CallerRunsPolicy: 在调用execute的线程里面执行此command,会阻塞入
  5. 用户自定义拒绝策略: 实现RejectedExecutionHandler,并自己定义策略模式

[CachedThreadPool 、 FixedThreadPool、SingleThreadPool]

  1. newSingleThreadExecutor: 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
    适用场景:任务少 ,并且不需要并发执行
  2. newCachedThreadPool: 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
    线程没有任务要执行时,便处于空闲状态,处于空闲状态的线程并不会被立即销毁(会被缓存住),只有当空闲时间超出一段时间(默认为60s)后,线程池才会销毁该线程(相当于清除过时的缓存)。新任务到达后,线程池首先会让被缓存住的线程(空闲状态)去执行任务,如果没有可用线程(无空闲线程),便会创建新的线程。
    适用场景:处理任务速度 > 提交任务速度,耗时少的任务(避免无限新增线程)
  3. newFixedThreadPool: 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  4. newScheduledThreadPool: 创建一个定长线程池,支持定时及周期性任务执行

[CopyOnWriteArrayList]

CopyOnWriteArrayList : 写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,然后在新的容器里面写,写完之后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,所以可以进行并发的读,但这是一种弱一致性的策略。

使用场景: CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

[AQS]

AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。
private volatile int state;//共享变量,使用volatile修饰保证线程可见性
  1. 2种同步方式:独占式,共享式。独占式如ReentrantLock,共享式如Semaphore,CountDownLatch,组合式的如ReentrantReadWriteLock
  2. 节点的状态
    CANCELLED,值为1,表示当前的线程被取消;
    SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
    CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
    PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
    值为0,表示当前节点在sync队列中,等待着获取锁。
  3. 模板方法模式 protected boolean tryAcquire(int arg) : 独占式获取同步状态,试着获取,成功返回true,反之为false protected boolean tryRelease(int arg) :独占式释放同步状态,等待中的其他线程此时将有机会获取到同步状态; protected int tryAcquireShared(int arg) :共享式获取同步状态,返回值大于等于0,代表获取成功;反之获取失败; protected boolean tryReleaseShared(int arg) :共享式释放同步状态,成功为true,失败为falseAQS维护一个共享资源state,通过内置的FIFO来完成获取资源线程的排队工作。该队列由一个一个的Node结点组成,每个Node结点维护一个prev引用和next引用,分别指向自己的前驱和后继结点。双端双向链表。
    a. 独占式:乐观的并发策略
    acquire
     a.首先tryAcquire获取同步状态,成功则直接返回;否则,进入下一环节;
    b.线程获取同步状态失败,就构造一个结点,加入同步队列中,这个过程要保证线程安全;
     c.加入队列中的结点线程进入自旋状态,若是老二结点(即前驱结点为头结点),才有机会尝试去获取同步状态;否则,当其前驱结点的状态为SIGNAL,线程便可安心休息,进入阻塞状态,直到被中断或者被前驱结点唤醒。
    release
    release的同步状态相对简单,需要找到头结点的后继结点进行唤醒,若后继结点为空或处于CANCEL状态,从后向前遍历找寻一个正常的结点,唤醒其对应线程。
  4. 共享式:
    共享式地获取同步状态.同步状态的方法tryAcquireShared返回值为int。
    a.当返回值大于0时,表示获取同步状态成功,同时还有剩余同步状态可供其他线程获取;
     b.当返回值等于0时,表示获取同步状态成功,但没有可用同步状态了;
     c.当返回值小于0时,表示获取同步状态失败。
  5. AQS实现公平锁和非公平锁
    非公平锁中,那些尝试获取锁且尚未进入等待队列的线程会和等待队列head结点的线程发生竞争。公平锁中,在获取锁时,增加了isFirst(current)判断,当且仅当,等待队列为空或当前线程是等待队列的头结点时,才可尝试获取锁。

[ Java里的阻塞队列]

7个阻塞队列。分别是
ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

添加元素
Java中的阻塞队列接口BlockingQueue继承自Queue接口。BlockingQueue接口提供了3个添加元素方法。
add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会抛出IllegalStateException异常
offer:添加元素到队列里,添加成功返回true,添加失败返回false
put:添加元素到队列里,如果容量满了会阻塞直到容量不满

删除方法
3个删除方法
poll:删除队列头部元素,如果队列为空,返回null。否则返回元素。
remove:基于对象找到对应的元素,并删除。删除成功返回true,否则返回false
take:删除队列头部元素,如果队列为空,一直阻塞到队列有元素并删除

[死锁,以及解决死锁]

死锁产生的四个必要条件
互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。

解决死锁
一是死锁预防,就是不让上面的四个条件同时成立。
二是合理分配资源。
三是使用银行家算法,如果该进程请求的资源操作系统剩余量可以满足,那么就分配。

[interrupted() 和 isInterrupted()的区别]

interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。
区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。
[原文链接 - interrupt()和线程终止方式]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值