Shared Lock(S锁) eXclusive Lock(X锁)
我们目前使用的锁都是独占锁(只有一个线程持有锁)
独占锁 | 共享锁 | |
读 + 读 | 互斥 | 不互斥 |
读 + 写 | 互斥 | 互斥 |
写 + 写 | 互斥 | 互斥 |
当业务中,读的次数 >> 写的次数,共享锁 优于 独占锁
synchronized 锁是独占锁
2.重入锁(ReentrantLock) VS 不可重入锁是否允许持有锁的线程成功请求到同一把锁
t1线程
lock1.lock(); t1 成功锁上了 lock1
...
lock1.lock(); 此时 lock1 处于已经锁上的状态 && 请求锁的还是 t1 线程
那么这次加锁能否成功?
允许成功:可重入锁
不允许成功:不可重入锁
实现角度:是否要记录当前是哪个线程用锁 锁定了
synchronized 锁是可重入锁
3.公平锁(fair) VS 不公平锁当一把锁处于 锁 状态时,队列中有很多请求锁失败的线程们 在等待,当处于某一时刻,锁打开了,刚好有一个后来的新线程来请求锁,这个线程优先获取到锁了,那么就说明这个锁的实现不公平。应该把锁交给最先等的线程,而不是刚好此时运气好的线程。
非公平锁实现简单(默认)
公平锁实现复杂
公平锁:严格按照请求锁的次序获取到锁
synchronized 锁是不公平锁
lock 中 通过传入fair = true/false 来控制是否是公平的
Lock lock = new ReentrantLock(true); // fair = true:使用公平锁模式
Lock lock1 = new ReentrantLock(false); // fair = false:使用不公平锁模式
Lock lock2 = new ReentrantLock(); // 默认情况下是不公平的
4.乐观锁 VS 悲观锁
翻译的问题:这都不是锁。严格来讲,这两个是实现并发控制的两种不同的方案,和“锁”的概念不是一个层级的概念
乐观锁:评估后,并发情况,多个线程同时修改一个共享资源的情况比较少见,可以采用轻量级(无锁lock-free)方式,进行并发控制
悲观锁:多个线程会频繁地修改同一个共享资源,必须使用互斥的方式(锁lock)来进行并发控制
5.锁的实现方案举个例子:
锁的实现导致的锁的种类
默认情况下,我们的锁的实现,是采用 OS 提供的锁(mutex 锁:互斥锁)
一旦请求锁失败,会导致当前线程(请求锁失败的线程)会放弃 CPU,进入阻塞状态,把自己加到锁的阻塞队列中,等待被唤醒。
必须进入到内核态,一旦放弃 CPU ,再到获取 CPU,时间相隔很久(站在 CPU 指令角度)
这种默认的互斥锁的成本较大。
需要一种不触发线程调度的锁的实现
硬件提供了 CAS 机制 -> OS提供了 CAS 机制 -> JVM 提供了 CAS 机制
6.互斥锁(mutex)VS 自旋锁(spin lock)lock:
boolean success = CAS(0x104,0,1);
if(success){return;}
// 加锁失败
互斥锁的方案:放弃 CPU... 引发线程调度。计算机只有一个核时的解决思路:早让持有锁的线程释放锁。
自旋锁的方案:
for(int i = 0;i < 1000;i++){ } // 强行执行一些没有任何用途的指令,不放弃 CPU ,占着CPU ,等锁被释放
更适用于现代计算机:多核的模式 + 一般来说,锁的持有时间不会很长(线程放弃CPU到线程再持有CPU的时间是 远大于 锁的持有时长的),即使当前线程占用了一个核,也没关系。很快,另外的核上的线程就会释放锁。
7.synchronized 锁的实现与优化策略:可重入的 + 不公平的 + 独占锁
实现:
(1)锁消除优化
Vector v = new Vector(); v...
前提:Vector 为了做到线程安全,每个方法都用 synchronized 修饰了
但实际上,我们的代码中只有主线程 -> 所有做线程保护的 *** 作都是无用功(加锁、释放锁)
编译器 + JVM 判断出只有一个线程时,就会消除掉所有锁的 *** 作,提升性能。
(2)锁粗化优化
前提:已经没有办法进行锁消除的情况下
for(int i = 0 ;i < 1000;i++) {
sync(lock) {
number++;
}
}
还原成指令集就是:加锁 -> n++ -> 解锁 -> 加锁 -> n++ -> 解锁 -> 加锁 -> n++ -> 解锁 ...
过于频繁的加解锁 *** 作,导致性能下降——锁的粒度太细了
优化:加锁 -> n++ -> n++ -> n++ -> 解锁 -> 加锁 -> n++ -> n++ -> n++ -> 解锁...
比如每三次n++解一次锁,具体加几次解锁看实际情况。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)