CS-LogN思维导图:记录CS基础 面试题
开源地址:https://github.com/FISHers6/CS-LogN
线程池相关类
能获取子线程的运行结果
互斥同步(锁)
非互斥同(原子类)
并发容器
无同步与不可变方案
降低资源消耗
提高响应速度
提高线程的可管理性
corePoolSize、maximumPoolSize、keepAliveTime、workQueue、threadFactory、handler
图示
线程添加规则
1.如果线程数量小于corePoolSize,即使工作线程处于空闲状态,也会创建一个新线程来运行新任务,创建方法是使用threadFactory
2.如果线程数量大于corePoolSize但小于maximumPoolSize,则将任务放入队列
3.如果workQueue队列已满,并且线程数量小于maxPoolSize,则开辟一个非核心新线程来运行任务
4.如果队列已满,并且线程数大于或等于maxPoolSize,则拒绝该任务,执行handler
图示(分别与3个参数比较)
常用线程池
newFixedThreadPool
newSingleThreadExecutor
newCachedThreadPool
newScheduledThreadPool
如何设置初始化线程池的大小?
可根据线程池中的线程
处理任务的不同进行分别估计
CPU 密集型任务
IO 密集型任务
使用线程池的注意事项
线程池的状态
线程池停止
shutdown
shutdownNow
线程池的组成
1.线程池管理器
2.工作线程
3.任务队列:无界、有界、直接交付队列
4.任务接口Task
图示
Executor家族
Executor顶层接口,只有一个execute方法
ExecutorService继承了Executor,增加了一些新的方法,比如shutdown拥有了初步管理线程池的功能方法
Executors工具类,来创建,类似Collections
图示
线程池实现任务复用的原理
线程池对线程作了包装,不需要启动线程,不需要重复start线程,只是调用已有线程固定数量的线程来跑传进来的任务run方法
添加工作线程
重复利用线程执行不同的任务
每个 Thread 维护着一个 ThreadLocalMap 的引用;ThreadLocalMap 是 ThreadLocal 的内部类,用 Entry 来进行存储;key就对应一个个ThreadLocal
get方法:取出当前线程的ThreadLocalMap,然后调用map.getEntry方法,把ThreadLocal作为key参数传入,取出对应的value
set方法:往 ThreadLocalMap 设置ThreadLocal对应值
initalValue方法:延迟加载,get的时候设置初始化
图示
value内存泄漏
原因:ThreadLocal 被 ThreadLocalMap 中的 entry 的 key 弱引用。如果 ThreadLocal 没有被强引用, 那么 GC 时 Entry 的 key 就会被回收,但是对应的 value 却不会回收,就会造成内存泄漏
解决方案:每次使用完 ThreadLocal,都调用它的 remove () 方法,清除value数据。
源码图示
引入目的
解决Runnable的缺陷
是什么如何使用
引入目的
常用方法
使用场景
注意点
Callable和Future的关系
Future相当于一个存储器,它存储未来call()任务方法的返回值结果
可以用Future.get方法来获取Callable接口的执行结果,在call()未执行完毕之前没调用get的线程会被阻塞
线程池传入Callable,submit返回Future,get获取值
FutureTask
FutureTask是一种包装器,可以把Callable转化成Future和Runnable,它同时实现了二者的接口。所以既可以作为Runnable任务被线程执行,又可以作为Future得到Callable的返回值
图示
final修饰变量
被final修饰的变量,意味着值不能被修改。
如果变量是对象,那么对象的引用不能变,但是对象自身的内容依然可以变化。
赋值时机
属性被声明为final后,该变量则只能被赋值一次。且一旦被赋值,final的变量就不能再被改变,如论如何也不会变。
区分为3种
final instance variable(类中的final属性)
final static variable(类中的static final属性)
final local variable(方法中的final变量)
为什么规定时机
final修饰方法(构造方法除外)
final修饰类
ABA问题
CAS+自旋,导致自旋时间过长
改进:通过版本号的机制来解决。每次变量更新的时候,版本号加 1,如AtomicStampedReference的compareAndSet ()
简介
Lock和Synchronized的异同点
相同点
不同点
Lock 有比 synchronized 更精确的线程语义和更好的性能;高级功能
1 实现原理不同
2 灵活性不同
3 等待时是否可以中断
可见性
悲观锁(互斥同步锁)
思想
实例
缺点
乐观锁
思想
实例
优缺点对比
对比
什么是可重入
可重入的好处
可重入锁ReentrantLock与非可重入锁ThreadPoolExecutor的Worker类对比
公平锁
介绍
优点
缺点
非公平锁
介绍
优点
缺点
优缺点对比
源码分析
排他锁
介绍
共享锁
介绍
ReentrantReadWriteLock
读写锁的作用
读写锁的规则
一把锁两种方式锁定
读线程插队策略(非公平下)
锁升级
引入场景
策略
适合场景
阻塞锁
思想
开销缺陷
自旋锁
思想
开销缺陷
源码分析
atomic包下的类基本都是自旋锁的实现
AtomicInteger的实现:自旋锁实现原理是CAS,Atomic调用Unsafe进行自增add的源码中的do-while循环就是一个自旋操作,使用CAS如果修改过程中遇到其它线程修改导致没有秀嘎四成功,就在while里死循环,直至修改成功
图示
适用场景
介绍
使用场景
JDK1.6 后对synchronized锁的优化
JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。
偏向锁
轻量级锁
重量级锁
自旋锁
自适应自旋锁
锁消除
锁粗化
写代码时的优化
常用方法
实现原理
缺点
引入目的/改进思想
设计思想
集合类历史
为什么需要
为什么不用HashMap
为什么不用Collection.synchronizedMap
数据结构与并发策略
JDK1.7
JDK1.8
1.7到1.8改变后有哪些优点
注意事项
引入目的
适合场景
读写规则
实现原理
缺点
为什么使用队列
并发队列关系图示
BlockingQueue阻塞队列
阻塞队列是局由自动阻塞功能的队列,线程安全;take方法移除队头,若队列无数据则阻塞直到有数据;put方法插入元素,如果队列已满就无法继续插入则阻塞直到队列里有了空闲空间
ArrayBlockQueue
LinkedBlockingQueue
PriorityBlockingQueue
SynchronousQueue
DelayQueue
非阻塞队列
ConcurrentLinkedQueue
选择合适的队列
边界上看
内存上看
吞吐量上看
控制并发流程的工具类,作用是帮助程序员更容易让线程之间相互配合,来满足业务逻辑
并发工具类图示
作用(事件)
常用方法
作用
常用方法
作用
常用方法
作用(线程)
常用方法
Exclusive(独占)
Share(共享)
核心三要素
1.sate
2.控制线程抢锁和配合的FIFO队列
3.期望协作工具类去实现的“获取/释放”等唤醒分配的方法策略
AQS的用法
AQS在CountDownLatch的应用
内部类Sync继承AQS
1.state表示门闩倒数的count数量,对应getCount方法获取
2.释放方法,countDown方法会让state减1,直到减为0时就唤醒所有线程。countDown方法调用releaseShared,它调用sync实现的tryReleaseShared,其使用CAS+自旋锁,来实现安全的计数-1
3.阻塞方法,await会调用sync提供的aquireSharedInterruptly方法,当state不等于0时,最终调用LockUpport的park,它利用Unsafe的park,native方法,把线程加入阻塞队列
总结
AQS在Semphore的应用
state表示信号量允许的剩余许可数量
tryAcquire方法,判断信号量大于0就成功获取,使用CAS+自旋改变state状态。如果信号量小于0了,再请求时tryAcquireShared返回负数,调用aquireSharedInterruptly方法就进入阻塞队列
release方法,调用sync实现的releaseShared,会利用AQS去阻塞队列唤醒一个线程
总结
AQS在ReentrantLock的应用
原文:https://www.cnblogs.com/fisherss/p/13191490.html