阻塞队列
结构:
Collection 接口
|
Queue 接口
|
BlockingQueue接口
|
实现类
ArrayBlockingQueue 底层数组,创建时需指定大小,无界。
LinkedBlockingQueue 底层链表,默认长度21亿。
SynchronousQueue 不存储元素,而是直接将生产出的元素丢给消费者。
阻塞队列常用API:
抛出异常 | 特殊值 | 阻塞 | 超时 | |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e, time,unit) |
移除 | remove() | poll() | take() | poll(time,unit) |
检查 | element() | peek() | / | / |
线程池
核心类:
Executor 接口
|
ExecutorService接口
|
实现类ThreadPoolExecutor
常见线程池:
-
newFixedThreadPool:
Executors.newFixedThreadPool(5)
创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,然后执行完线程池再把这个线程回收,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。
-
newSingleThreadExecutor:
Executors.newSingleThreadExecutor()
这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行。
-
newCachedThreadPool:
Executors.newCachedThreadPool
创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。
-
newScheduledThreadPool(int corePoolSize)(了解)
创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。
示范代码:
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
// 指定线程数
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 只有一个
ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
// 自己能忙过来就自己干,忙不过来就请帮手
ExecutorService threadPool3 = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
threadPool3.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行任务");
}
});
Thread.sleep(500);
}
}
}
线程池 重要参数:
这三个线程池,
- 底层都是用的ThreadPoolExecutor
- 并且都是7个参数
- 并且调动方法都一样
- 使用的LinkedBlockingQueue 底层链表,默认长度21亿
线程池的7个参数包括:
-
int corePoolSize
-
int maxiumPoolSize
-
long keepAliveTime
-
TimeUnit unit
-
BlockingQueue workQueue
-
ThreadFactory threadFactory
-
RejectedExecutionHandler handler
-
corePoolSize:线程池核心池大小,也就是常驻的核心线程数
-
maxiumPoolSize:最大池大小,也就是最大线程数
-
keepAliveTime:多于的空闲线程的存活时间,当前线程池数量超过corePoolSize时,当高峰期过去,空闲时间达到keepAliveTime还没线程需要处理任务.,多余空闲线程会被销毁,相当于从maxiumPoolSize缩容回corePoolSize,减少资源消耗
-
unit:keepAliveTime的单位
-
workQueue:放任务的,候客区,用于排队等候队列。
-
threadFactory:默认工厂
-
RejectedExecutionHandler:拒绝策略
RejectedExecutionHandler 的四种拒绝策略:
- AbortPolicy(默认):直接抛出RejectedExecutionException异常,组织系统正常运行。
- CallerRunsPolicy:调用者运行一种调用机制,该策略不会抛出异常,而是将某些任务退回给调用者,从而降低流量。
- DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中再尝试提交任务。
- DiscardPolicy:直接丢弃任务,不予处理,如果允许任务丢失,这是最好的解决方案。
线程池都有哪些状态?
线程池有5种状态:Running、ShutDown、Stop、Tidying、Terminated。
线程池各个状态切换框架图:
线程池中 submit()和 execute()方法有什么区别?
- 接收的参数不一样
- submit有返回值,而execute没有
- submit方便Exception处理
工作的线程的数量是怎么算的?经验
- CPU型 这些多线程程序在进行大量的运算 线程数=CPU核数+1
- IO型 没有大量的计算 CPU的2倍或3倍
示例代码:
public class ThreadPoolDemo {
public static void main(String[] args) throws InterruptedException {
// 指定线程数
ExecutorService threadPool = Executors.newFixedThreadPool(5);
// 只有一个
ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
// 自己能忙过来就自己干,忙不过来就请帮手
ExecutorService threadPool3 = Executors.newCachedThreadPool();
// 工作中自定义线程池
ExecutorService threadPoolExecutor = new ThreadPoolExecutor(2,4,1L,
TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 7; i++) {
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行任务");
}
});
}
}
}
手写线程池(了解)
线程池是Executors,底层是LinkedBlockingQueue,而LinkedBlockingQueue的底层是一个链表,大小是21亿
public class FixedThreadPool {
// 阻塞队列
private BlockingQueue<Runnable> taskQueue;
// 集合
private List<Worker> works;
public FixedThreadPool(int poolSize,int taskNum){
taskQueue = new LinkedBlockingDeque<>(taskNum);
works = new ArrayList<>();
for (int i = 0; i < poolSize; i++) {
Worker w = new Worker(this);
w.start();
works.add(w);
}
}
public boolean execute(Runnable r){
return taskQueue.offer(r);
}
private static class Worker extends Thread{
FixedThreadPool pool = null;
public Worker(FixedThreadPool pool){
this.pool = pool;
}
public void run(){
while(true){
Runnable task = null;
try {
task = pool.taskQueue.take();
if(task!=null){
task.run();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class ThreadPoolDemo {
public static void main(String[] args) {
// 自己手写线程池
FixedThreadPool threadPool4 = new FixedThreadPool(5,8);
for (int i = 0; i < 10; i++) {
threadPool4.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行任务");
}
});
}
}
}
线程池执行任务,调用execute方法,任务提交到阻塞队列中,taskQueue.offer(r)
提交之前new了poolSize,准备好线程数,w.start();准备好了5个线程,这些线程进入run方法的while循环中,从阻塞队列里拿task,拿出来的就是提交的task。