9.阻塞队列BlockingQueue
什么时候会使用堵塞队列:多线程并发处理,线程池
四组API
方式 | 抛出异常 | 有返回值(T or F),不抛出异常 | 阻塞,一直等待 | 阻塞,超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer(,,) |
移除 | remove() | pull() | take() | pull(,) |
检测队首元素 | element() | peek() | - | - |
public class TestBlockingQueue {
public static void main(String[] args) throws InterruptedException {
test4();
}
// 抛出异常:java.lang.IllegalStateException: Queue full
public static void test1(){
// 队列的大小为3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
// add()方法返回boolean值
boolean flag1 = blockingQueue.add("a");
boolean flag2 = blockingQueue.add("b");
boolean flag3 = blockingQueue.add("c");
// !!! add添加元素超过队列的长度会抛出异常java.lang.IllegalStateException: Queue full
boolean flag4 = blockingQueue.add("d");
System.out.println(blockingQueue.element());// 获得队首元素
System.out.println("=========");
// remove()返回本次移除的元素
Object e1 = blockingQueue.remove();//a
Object e2 = blockingQueue.remove();//b
Object e3 = blockingQueue.remove();//c
//!!! 队列中没有元素仍继续移除元素会抛出异常java.util.NoSuchElementException
Object e4 = blockingQueue.remove();
}
// 有返回值,不抛出异常
public static void test2(){
// 队列的大小为3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
// offer返回boolean值
boolean flag1 = blockingQueue.offer("a");
boolean flag2 = blockingQueue.offer("b");
boolean flag3 = blockingQueue.offer("c");
//boolean flag4 = blockingQueue.offer("d");// offer添加元素超过队列的长度会返回false
System.out.println(blockingQueue.peek());// 获得队首元素
System.out.println("=========");
// poll()返回本次移除的元素
Object poll1 = blockingQueue.poll();
Object poll2 = blockingQueue.poll();
Object poll3 = blockingQueue.poll();
Object poll4 = blockingQueue.poll();// 队列中没有元素仍继续移除元素会打印出null
}
// 阻塞,一直等待
public static void test3() throws InterruptedException {
// 队列的大小为3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
// put没有返回值
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//blockingQueue.put("d");// put添加元素超过队列的长度会一直等待
System.out.println("=========");
// take()返回本次移除的元素
Object take1 = blockingQueue.take();
Object take2 = blockingQueue.take();
Object take3 = blockingQueue.take();
Object take4 = blockingQueue.take();// 队列中没有元素仍继续移除元素会一直等待
}
// 阻塞,超时等待
public static void test4() throws InterruptedException {
// 队列的大小为3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
// offer返回boolean值
boolean flag1 = blockingQueue.offer("a");
boolean flag2 = blockingQueue.offer("b");
boolean flag3 = blockingQueue.offer("c");
// offer添加元素超过队列的长度会返回false;并且等待指定时间后推出,向下执行
boolean flag4 = blockingQueue.offer("d", 2, TimeUnit.SECONDS);
System.out.println("=========");
// poll()返回本次移除的元素
Object poll1 = blockingQueue.poll();
Object poll2 = blockingQueue.poll();
Object poll3 = blockingQueue.poll();
// 队列中没有元素仍继续移除元素会打印出null,等待指定之间后退出。
Object poll4 = blockingQueue.poll(2,TimeUnit.SECONDS);
}
}
SynchronousQueue同步队列
SynchronousQueue是BlockingQueue的一个实现类
没有容量。
进去一个元素,必须等待取出这个元素后,才能放下一个元素。put()、take()
10.线程池
线程池:三大方法,7大参数,4种拒绝策略
创建,销毁浪费资源
池化技术 优化资源的使用
事先准备好一些资源,有人用就来这里拿,用完后,还给我。
好处:
- 降低资源的消耗
- 提高响应速度
- 方便管理
线程可以复用,可以控制最大并发量,管理线程
三大方法
Executors:执行人
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE(约为21亿),可能会创建数量非常多的线程,甚至OOM。
public interface Executor {
void execute(Runnable command); //也是Runnable接口
public class demo1 {
public static void main(String[] args) {
//Executors工具类,三大方法
// ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个固定大小得线程池
//ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩,线程数可变
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
for (int i = 0; i < 10; i++) {
//使用了线程池之后,使用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "ok");
});
}
//线程池用完,程序结束,关闭线程池
try {
threadPool.shutdown();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
每一个的执行结果:
Executors.newSingleThreadExecutor();//单个线程
pool-1-thread-1ok
pool-1-thread-1ok
pool-1-thread-1ok
pool-1-thread-1ok
pool-1-thread-1ok
pool-1-thread-1ok
pool-1-thread-1ok
pool-1-thread-1ok
pool-1-thread-1ok
pool-1-thread-1ok
Executors.newFixedThreadPool(5);//创建一个固定大小得线程池
pool-1-thread-3ok
pool-1-thread-2ok
pool-1-thread-5ok
pool-1-thread-4ok
pool-1-thread-1ok
pool-1-thread-4ok
pool-1-thread-5ok
pool-1-thread-3ok
pool-1-thread-4ok
pool-1-thread-1ok
ExecutorService threadPool = Executors.newCachedThreadPool();//可伸缩,线程数可变
pool-1-thread-2ok
pool-1-thread-1ok
pool-1-thread-3ok
pool-1-thread-10ok
pool-1-thread-8ok
pool-1-thread-9ok
pool-1-thread-5ok
pool-1-thread-6ok
pool-1-thread-4ok
pool-1-thread-7ok
七大参数
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
三大方法底层都调用得是ThreadPoolExecutor!!
不推荐使用的原因:
//七个参数
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//线程池中允许的最大线程数
long keepAliveTime,//超时了,没人用就会释放
TimeUnit unit,//超时单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂,创建线程,一般不用动
RejectedExecutionHandler handler//拒绝策略) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
-
corePoolSize,//核心线程池大小 就是最小线程池大小
-
keepAliveTime:线程的最大生命周期,这里的生命周期有两个约束条件
一:该参数针对的是超过corePoolSize数量的线程
二:处于非运行状态的线程。即非核心线程数存活时间
-
一般情况就是核心线程去处理
当线程过来 阻塞队列等待(候客区)的都满了 才会去开启非核心线程(测过)
如下图就是6个及以上 才会去创建非核心线程
-
最大的承载就是阻塞队列+最大线程数
如下图就是8个
-
注意还有一个
largestPoolSize
,记录了曾经出现的最大线程个数。因为setMaximumPoolSize()
可以改变最大线程数。 -
keepAliveTime 超时等待:下图 345是非核心线程 ,keepAliveTime 时间内没人使用就自动关闭了
new ThreadPoolExecutor(2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
与上图对应的手动创建的 线程池
四种拒绝策略
new ThreadPoolExecutor.AbortPolicy(); // 抛出异常
new ThreadPoolExecutor.CallerRunsPolicy();// 哪来的去哪(主线程来的,就回去让主线程执行,)
new ThreadPoolExecutor.DiscardPolicy();// 丢掉任务,不抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy();// 尝试和最早的竞争,竞争失败了也丢掉任务,也不抛出异常
for (int i = 0; i < 10; i++) {
//使用了线程池之后,使用线程池来创建线程
threadPool.execute(() -> { CallerRunsPolicy 让主线程去跑里面的run方法,让主线程去代办 线程池不管了
System.out.println(Thread.currentThread().getName() + "ok");
});
}