JUC并发队列及应用
文章目录
- 1.队列类型
- 2.无界非阻塞队列ConcurrentLinkedQueue:
- 3.无界阻塞队列LinkedBlockingQueue:
- 4.有界阻塞队列ArrayBlockingQueue:
- 5.带优先级无界阻塞队列PriorityBlockingQueue:
- 6.无界阻塞延迟队列DelayQueue:
- 7.额外补充
1.队列类型
阻塞队列:为了保证线程安全采用阻塞线程方式操作队列。
非阻塞队列:不阻塞线程操作队列。
有界队列: 队列长度有限制。
无界队列: 队列长度无限制。
2.无界非阻塞队列ConcurrentLinkedQueue:
实现结构:单向链表。
实现线程安全方式:CAS【非阻塞】,poll出队和add/offer方法入队都是采用CAS机制
来实现线程安全。
典型应用:Tomcat 中NioEndpoint使用ConcurrentLinkedQueue来接收请求。
特点:性能优越,适用于读多写少
的场景。
3.无界阻塞队列LinkedBlockingQueue:
实现方式:单向链表
实现线程安全方式:take 和 put 对应【阻塞
方法】,poll和offer对应【非阻塞
方法】
如果take执行过程中队列为空/put队列已满则会陷入阻塞。poll/offer不会阻塞,而是直接返回null/false。
采用两个独占锁
来实现头尾节点操作原子性,并都配备了一个条件队列
,存放被阻塞的线程,结合入队出队可实现生产消费模型。【入队出队可同时运行
】
典型应用:JDK 线程池等
特点:容量可达Integer.MAX_VALUE值,先进先出,存取分离,并且能保证节点前后置不乱,删除节点时加两把锁,性能优越
。
4.有界阻塞队列ArrayBlockingQueue:
实现方式:数组
实现线程安全方式: 全局独占锁
典型应用:异步日志打印等
特点:同时只能有一个线程
进行入队或者出队操作。size计算精确。
5.带优先级无界阻塞队列PriorityBlockingQueue:
实现方式:平衡二叉树堆 数组进行存储
实现线程安全方式:一个独占锁
典型应用:任务调度,资源分配、事件处理等
特点:同时只能有一个线程进行入队或出队,但是由于是无界的,可一直put,不会陷入阻塞,内置使用CAS算法进行扩容
操作,可自定以优先级比较规则。
6.无界阻塞延迟队列DelayQueue:
实现方式:优先级队列,且元素必须要实现Delayed接口
实现线程安全方式: 独占锁
典型应用:定时发送邮件、定期数据清理、订单支付、延时发送消息、缓存过期清除等
特点:每个元素都有个过期时间、只有过期元素才会出队列。可实现延时功能。也可自定义元素比较规则。
7.额外补充
1.JDK7
新增ThreadLocalRandom
类,弥补传统Random
类在多线程高并发请求下性能缺陷。
Random采用CAS
机制来确保seed种子一定是基于上个seed种子生成的新种子,然后参与计算生成随机数。
protected int next(int bits) {long oldseed, nextseed;AtomicLong seed = this.seed;do {oldseed = seed.get();nextseed = (oldseed * multiplier + addend) & mask;} while (!seed.compareAndSet(oldseed, nextseed));return (int)(nextseed >>> (48 - bits));}
CAS机制在高并发多线程下的缺陷
就是不断自旋获取锁,大量占用CPU
为了解决这个问题,引入了ThreadLocalRandom
类。将seed挪到Thread实例对象中。每个Thread都有一分属于自己的seed 也就是ThreadLocalRandomSeed
。
带大家看下这个ThreadLocalRandom
类生成随机数nextInt
()方法执行逻辑
@Overridepublic int nextInt() {return mix32(nextSeed());}// 因此这个nextSeed() 要保证原子性。final long nextSeed() {Thread t; long r; // read and update per-thread seedU.putLong(t = Thread.currentThread(), SEED,r = U.getLong(t, SEED) + (t.threadId() << 1) + GOLDEN_GAMMA);return r;}// 逻辑计算是固定的。private static int mix32(long z) {z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);}
SEED来源于Thread 类中threadLocalRandomSeed 值
。由于该是线程独立私有,因此多线程情况下不会发生生成同样随机数的情况,同时也避免了CAS所带来的锁竞争引发的CPU资源损耗。
2.SimpleDateFormat线程不安全,多线程情况下建议放入ThreadLocal
中,记得使用完手动删除
,避免内存泄漏
。
3.Timer类定时任务执行多个TimerTask,如果存在一个方法抛出异常则会自动停止所有task,解决方案就是使用ScheduledThreadPoolExecutor
中的schedule
方法执行多个task。
4.需要复用且会被下游方法修改的参数要进行深复制,否则会出现错误结果。引用类型作为集合作为另一个集合的构造函数的参数,【引用传递】使用同一份引用,此时要采用深复制
。
5.线程池和线程构建需要补充业务相关的名称
,便于排查异常。
6.线程池关闭记得调用shutdown
关闭线程池。
7.线程池使用FutureTask
,为了避免在DiscardPolicy/DiscardOldestPolicy策略下调用get方法陷入阻塞,建议使用带超时时间的get
方法。