文章

锁的使用

锁的使用

Concurrent.util常用类

CyclicBarrier:

假设又只有的一个场景:每一个线程代表一个跑步运动员,当运动员都准备好后,才一起出发,只要有一个人没有准备好,大家都要等待。

CountDownLacth使用:

常用于监听某些初始化操作,等初 始化执行完毕后,通知主线程继续。

CountDownLacth countDownLacth = new CountDownLacth(2); 参数表示等待多少个countDown()方法

countDownLacth.await(); 通知在等待的线程

注意 CyclicBarrier 与 CountDownLacth 的区别:

CyclicBarrier 是针对的多个线程

CountDownLacth 只针对一个线程,当获得反馈后,该线程可以继续执行

Callable 和 Future 使用:

Future 模式非常适合处理很耗时的系统程序,提高系统的响应速度。

submit 和 execute 的区别:

1.submit可以传入实现Callable接口的实例对象

2.submit方法有返回值

FutureTask中的get() 方法会异步执行

Semaphore信号量非常适合高并发访问,新系统在上线之前,要对系统的访问量进行评估。

评估标准不能太大也不能太小

若太大,会浪费资源;若太小,在高峰值时的访问量会直接压垮系统。

PV(Page view):网站总访问量

UV(unique Visitor):访问网站的一台电脑客户端为一个访客

QPS(query per second):即每秒查询数,qps很大程度上代表了系统业务上的繁忙程度,每次请求的别后可能对应多次的磁盘I/O

RT(response time):即请求的响应时间,直接说明前端用户的体验,因此任何系统设计师都想降低RT时间

如何解决高并发:

1.网络层面

2.服务器层面

2.1通过ngix做负载均衡

2.2在业务上模块化,进行分流

3.java层面,限流(可以通过 Semaphore 信号量进行限制)

容量评估:遵循80/20原则:80%的请求再20%的时间内达到

可以根据8/2原则计算出峰值qps

峰值qps = (总PV * 80%) / (606024)*20%

Semaphore 可以控制系统的流量:

拿到信号的线程可以进入,否则就等待,通过acquire() 和 release() 获得和释放访问许可

锁:

synchronized关键字来实现线程间的同步互斥工作。

Lock对象,他们具有比synchronized更为强大的功能,并且有嗅探锁定、多路分支等功能

重入锁(ReentrantLock)

在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,不然会造成锁永远无法释放,其他线程永远进不来的结果。

锁与等待/通知

在使用lock的时候,可以使用一个新的等待通知类,他就是Condition,这个Condition一定是针对某一把具体的锁。也就是只有在锁的基础上才会产生Condition

多Condition

我们可以通过一个Lock对象创建多个Condition。

公平锁和非公平锁:

Lock lock = newReentrantLock(boolean isFair)

Lock用法:

tryLock():尝试获得锁,获得结果返回true/false

isFair():是否是公平锁

getHoldCount():查询当前线程保持此锁的个数,也就是条用lock()的次数

lockInterruptibly():优先响应中断的锁

getQueueLength():返回正在等待获取次所的线程数

getWaitQueueLength():返回等待与锁定相关的给定条件Condition的线程数

  • *

**

ReentrantReadWriteLock(读写锁):

核心:实现读写分离的锁。在高并发访问下,尤其是读多写少的场景下,性能要远高于重入锁

synchronized 和 ReentrantLock 中,我们知道,同一时间内,只能有一个线程进行访问被锁定的代码。

而读写锁不同,其本质是分成两个锁,即读锁、写锁。在读锁下,多个线程可以并发的进行访问,当时在写锁的时候吗,只能一个一个的顺序访问

口诀:读读共享,写写互斥,读写互斥

锁优化总结:

1.避免死锁

2.减小锁的时间

3.减小锁的粒度

4.锁的分离

5.尽量使用无锁的操作。如原子操作(Atomic类),volatile关键字

分布式锁:

本文由作者按照 CC BY 4.0 进行授权