自己参考大牛博客及视频写了一些关于并发的感悟,高并发的处理思路,无外乎以下几种

1 代码层面:锁优化措施(见本文内容)、尽量简化事务和减少事务

2 应用层面:缓存 队列 限流 熔断 

3数据库层面:分库分表 读写分离

JDK常见并发包处理工具中,ReentrantLock、countdownlanth、currenthasp、AQS源码一定要多读多看,理解里面的设计精髓。带着问题去思考,去看源码,会更加有效。

一:ReentrantLock源码分析:

阻塞队列是利用ReentrantLock配合condition中的asigl、await方法实现的

读写分离锁:读锁是利用到了共享模式,写锁是用到了独占模式,与synchronize相比,可以避免读读互排斥,降低了锁的范围

1.Java并发库中ReetrantReadWriteLock实现了ReadWriteLock接口并添加了可重入的特性

2.ReetrantReadWriteLock读写锁的效率明显高于synchronized关键字

3.ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的

4.ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁

设置了这个 State 变量,我们之前分析过 AQS 的源码,这个变量可以说是 AQS 实现的核心,通过控制这个变量,能够实现共享共享锁或者独占锁。

那么,如果让我们来设计这个CountDownLatch ,我们该如何设计呢?

事实上,很简单,我们只需要对 state 变量进行减 1 操作,直到这个变量变成 0,我们就唤醒主线程。

将当前线程包装成一个 Node 对象,加入到 AQS 的队列尾部。

如果他前面的 node 是 head ,便可以尝试获取锁了。

如果不是,则阻塞等待,调用的是 LockSupport.park(this);

CountDown 的 await 方法就是通过 AQS 的锁机制让主线程阻塞等待。而锁的实现就是通过构造器中设置的 state 变量来控制的。当 state 是 0 的时候,就可以获取锁。然后执行后面的逻辑。

总的来说,CountDownLatch 还是比较简单的。说白了就是通过共享锁实现的。在我们的代码中,只有一个线程会阻塞,那就是我们的主线程,其余的线程就是在不停的释放 state 变量,直到为 0。从 AQS 的角度来讲,整个工作流程如下图:

简单的一个流程图,CountDownLatch 就是通过使用 AQS 的机制来实现倒计时门栓的。

作者:莫那一鲁道

二:ReentrantLock源码分析: 

公平锁与非公平锁是如何实现的?

多个线程,只有一个线程获取锁时,其他线程是如何被唤醒的

如何线程安全的修改锁状态位?

得不到锁的线程,如何排队?

与synchronize区别

ReentrantLock继承AQS独占式方法,自旋锁的思想是:假设有1000个线程等待获取锁,是根据CAS及volatile 修改的状态变量进行判断的,当前线程的锁释放后,只会通知队列中的第一个线程去竞争锁,减少了并发冲突。(ZK的分布式锁,为了避免惊群效应,也使用了类似的方式:获取不到锁的线程只监听前一个节点)

为什么说JUC中的实现是基于CLH的“变种”,因为原始CLH队列,一般用于实现自旋锁。而JUC中的实现,获取不到锁的线程,一般会时而阻塞,时而唤醒。阻塞唤醒是通过locksupport中的park、UNpark方法实现的

流程处理步骤: 

基于CAS尝试将state(锁数量)从0设置为1

A、如果设置成功,设置当前线程为独占锁的线程;

B、如果设置失败,还会再获取一次锁数量,

B1、如果锁数量为0,再基于CAS尝试将state(锁数量)从0设置为1一次,如果设置成功,设置当前线程为独占锁的线程;

B2、如果锁数量不为0或者上边的尝试又失败了,查看当前线程是不是已经是独占锁的线程了,如果是,则将当前的锁数量+1;如果不是,则将该线程封装在一个Node内,并加入到等待队列中去。等待被其前一个线程节点唤醒。


实现锁的关键在于:

通过CAS操作与volatile变量互相配合,线程安全的修改锁标志位

基于CLH队列,实现锁的排队策略

公平锁、非公平锁都是静态内部类,区别就在于hasQueuedPredecessors这个方法,

因此公平锁和非公平锁的区别 多了需要判断当前线程是否在等待队列首部的逻辑 Java高并发处理总结_读写锁

公平锁、非公平锁都是静态内部类,区别就在于hasQueuedPredecessors这个方法,

因此公平锁和非公平锁的区别 多了需要判断当前线程是否在等待队列首部的逻辑

Java高并发处理总结_公平锁_02

Java高并发处理总结_读写锁_03

Java高并发处理总结_公平锁_04

Java高并发处理总结_公平锁_05

Java高并发处理总结_公平锁_06

Java高并发处理总结_公平锁_07

Java高并发处理总结_公平锁_08

Java高并发处理总结_公平锁_09

Java高并发处理总结_读锁_10