并发编程@并发编程高级


* JDK多任务执行框架
* Executor框架
Executor是一个线程工厂,可以创建特定功能的线程池
Executor创建线程池的方法:
* newFixedThreadPool()
创建一个【固定数量】的线程池,该线程池的线程数量始终不变
* 当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列当中,等待由空闲的线程去执行
* newSingleThreadPool()
创建一个【只有一个线程】的线程池
* 当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列当中,等待由空闲的线程去执行
* newCacheThreadPool()
创建一个【可以根据实际情况调整线程个数】的线程池,不限制最大线程数量
* 当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会创建一个新的线程去执行。
* 对于空闲的线程,超过60秒会自动回收
* newScheduledThreadPool()
创建一个SchededExecutorService对象,可以指定线程的数量
备注:
各种ThreadPool实际上都是new ThreadPoolExecutor()对象,只是根据传递参数的不同,导致它实例化出的线程池对象具有特定的功能
* 自定义ThreadPool
* 通过new ThreadPoolExecutor,传递特定参数可以创建自定义的线程池对象
public ThreadPoolExecutor(
int corePoolSize, // 当前核心线程数
/* 对于jdk提供了线程池,初始化时就初始化的线程数。自定义的时候不会初始化 */

int maximumPoolSize, // 最大线程数
long keepAliveTime, // 线程空闲时保持活跃的时间
TimeUnit unit, // 指定时间单位
BlockingQueue<Runnable> workQueue // 指定缓冲队列,当线程都忙,可以将任务暂存
ThreadFactory threadFactory, //
RejectedExecutionHandler handler // 拒绝执行的方法
) {}
* 这个方法对于队列是什么类型的比较关键:
* 使用有界队列时,若有新的任务需要执行:
若线程池实际线程数小于corePoolSize,则优先创建线程
若线程池实际线程数大于corePoolSize:
* 若队列未满,则会将任务加入队列,等由线程空闲的时候才执行
* 若队列已满:
* 若总线程数不大于maximumPoolSize,则创建线程去执行
* 若总线程树大于maximumPoolSize,则执行拒绝策略
* 使用无界队列时,LinkedBlockingQueue,除非系统资源耗尽,否则不会存在任务入队失败的情况。
若线程池实际线程数小于corePoolSize,则创建线程
若线程池实际线程数大于corePoolSize,则不再创建线程,如果此时还有任务,则加入等待队列
* 拒绝策略
AbortPolicy:直接抛出异常组织系统正常工作
CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务
DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前的任务
DiscardPolicy:丢弃无法处理的任务,不给予任何处理
* 自定义拒绝策略
实现RejectExecutionHandler接口

* Concurrent.util工具包下常用【工具类】
* CyclicBarrier
假设由如下场景:每一个线程代表一个运动员,当运动员都准备好,才一起出发,只要由一个人每准备好,大家就都不出发
* CountDownLatch(latch寄存器)
它经常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作
* CyclicBarrier和CountDownLatch的区别
* CyclicBarrier是多个线程等待,多个线程通知,满足个数的话,多个线程执行各自的线程逻辑
* CountDownLatch是一个线程等待,多个线程发出通知,满足个数的话,这个线程执行

* 如何解决高并发【粗粒度进行分流、细粒度进行限流】
* 服务器方面(分流)-- 粗粒度 备注:技术厉害的可以在nginx上进行限流
* 业务上,根据模块划分,使用分布式
* 技术上,使用nginx/lvs+haproxy搭集群,负载均衡(轮询),进行分流
* Java技术方面(限流)-- 细粒度
* 信号量进行限流
* 线程池(高并发队列)
* 高并发重入锁、读多写少读写锁
* 其他
* 使用缓存
* 页面静态化
* 文件系统存储图片资源等
* 分表(避免多表查询)

* 信号量(Semaphore)
* 相关概念
* PV:page view,网站总访问量
* UV:unique visitor,每天中的客户访问量,一个客户ID一天内只记录一次
* QPS:query per second,每秒查询数,即吞吐量
* RT:response time,请求响应时间
* 80/20原则:80%的访问请求将在20%的时间内达到
* 一个系统的峰值
* QPS = (总PV * 80%) / (60 * 60 * 24 * 20%)
* Semaphore可以控制系统的流量:
* 拿到信号量的线程可以进入,否则就等待。通过acquire()和release()获取和释放访问许可

* 锁
* ReentrantLock -> condition [await/signal]
* Lock用法:
* 锁的嗅探:
* tryLock(): 尝试获得锁,获得结果用true、false返回
* tryLock(): 在给定时间内尝试获得锁,获得结果用true、false返回
* isFair(): 是否是公平锁
* isLocked(): 是否锁定
* getHoldCount(): 查询当前线程保持此锁的个数,也就是调用lock()的个数
* lockInterruptibly():优先响应中断的锁
* 公平锁、非公平锁
Lock lock = new ReentrantLock(Boolean isFair);
* 默认是非公平锁,false即非公平、true即公平
*
公平:先调用先上锁,有序[需要维护顺序,浪费性能]
非公平:按CPU分配上锁,无序[效率更高]

* 读写锁
* ReentrantReadWriteLock
* ReadWriteLock
* ReadLock
* ReadWriteLock
* 核心就是实现读写分离的锁,在高并发、尤其是读多写少的情况下,性能远远高于ReentrantLock
* 原则:读读共享、写写互斥、读写互斥
* Lock比Synchronize有什么优势?
* 1.8前,Lock性能比Synchronize好,1.8后,对synchronize进行优化
* Lock必须在final中释放锁,它不会自动释放锁,而synchronize会自动释放锁
* Lock更灵活,可以有多个condition来操作线程通信
* 锁的优化总结
* 避免死锁
* 减少锁的持有时间
* 减小锁的粒度
* 锁的分离
* 尽量使用无锁的操作,如原子操作(Atomic系列类),volatile关键字

posted @ 2019-12-28 15:23  默月  阅读(139)  评论(0编辑  收藏  举报