JUC笔记(二)
读写锁
分为写锁(独占锁)和读锁(共享锁)
读锁虽然跟不加锁效果差不多,但是必须加,因为读锁与写锁互斥,可以避免脏读。
阻塞队列 BlockingQueue
-
当队列是满的,此时再添加元素会阻塞
-
当队列是空的,此时再要取就会被阻塞
双端队列Deque
从两头都可以出入的队列,一般线程池底层使用双端队列,可以提高性能。
阻塞队列的4组API
| 方法\超过阈值时 | 抛出异常 | 返回一个boolean(存)或null(取),不抛出异常 | 等待设定时间,超时则返回boolean(存)或者null(取) | 一直等待 |
|---|---|---|---|---|
| 插入 | add() | offer(e) | offer(e,time,TimeUnit) | put() |
| 取出 | remove() | poll() | poll(time,TimeUnit) | take() |
| 检查 | peek() | - | - |
同步队列
SynchronousQueue 同步队列,容量只有1。
每一个put操作就需要有一个take操作。
线程池
最大的好处:线程复用
三大方法
FixedThreadPool,SingleThreadPool,CachedThread 了解就可以,实际不用。有oom风险。=>因为其底下默认使用的队列,长度是integer的长度:21亿。
七大参数
public ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//空闲线程存活时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//阻塞队列* 必须设置队列大小 且推荐使用Deque双端队列,性能更高
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//拒绝策略
) {
四大拒绝策略
AbortPolicy 线程池超出阈值,抛出异常,抛弃任务 CallerRunsPolicy 线程池超出阈值,让调用线程池的线程自己处理该任务 DiscardOldestPolicy 线程池超出阈值,尝试获取任务,不一定处理 DiscardPolicy 线程池超出阈值,不抛出异常,抛弃任务
自定义线程,最大线程数怎么设置好?
CPU密集型:根据CPU的处理器数量来定,保证最大效率。
可以根据代码获取CPU核数:
IO密集型:只要线程数大于常用IO线程数即可;
函数式接口
所有的函数式接口都能用lambda表达式简化
java.util.function
只有一个输入参数,和一个输出参数=>函数式接口
Function<String> function = (str)->{return str++;};
java.util.Predicate
只有一个输入参数,返回一个boolean =>断定型接口
Predicate<String> p =(str)->{return str.isEmpty();};
java.util.Supplier
没有参数,返回内容 =>提供者接口
Supplier<String> stringCallable = () -> {
return "intresting";
};
java.util.Consumer
有参数,没有返回值 =>消费者接口
Consumer<Integer> consumer = (str) -> {
str++;
};
流式计算
多练习!
分支合并
什么是forkjoin
与MapReduce一样。先拆分运行,后递归总结结果。
思想:将任务分解成很多小任务,再将结果逐层汇总,获取最终结果
前提:forkjoin 一定是用在大数据的情况下
工作原理:工作窃取
底层维护一个双端队列,一个线程执行完,会去另一个队列底下继续执行。=>也会导致问题:资源争夺。
并行流计算
使用并行流可以进行多种快速的计算
异步回调 Future
一个未来的get方法返回的结果类型。
future的get方法在获得结果前可以阻塞等待,且不只是能够返回内容,还可以取消,也会提供方法来确定任务是已完成或被取消。