【 悲观锁与乐观锁 】:
【悲观锁】:
一定会被别人打断;我必须得上锁。synchronized就是悲观锁。
【乐观锁】:
乐观锁又称之为——无锁、自旋锁、CAS 。
厕所里的人认为不会有其他人来上厕所和我竞争。
【举例解释CAS操作】:
//还是拿多个线程访问 n++ 为例。
【往回写的时候要进行判断】:
判断是否依然为0(和拿之前的值一样),如果是,就说明中间没有线程来动它。
【如果发现不想等】:
判断的时候如果发现已经被改成8了,这个时候就得重新读 , 重新把8读过来,进行++操作,然后再去判断看一看是否依然是8,如此一直循环,直到有一次发现——没有其他线程修改,那么就成功了。
【CAS中的ABA问题】:
此0非彼0问题
其他线程修改数次最后值和原值相同。
//有些情况下,我们必须要解决ABA问题。——拿的是引用类型的话,引用指向的内容变化了。
【解决方法】:
加上一个version即可。规定任意的操作都会让这个version的值加加。
【版本的类型】:
(1)带时间戳 / 带数字
(2)布尔类型
【CAS的底层原子性保障】:
compare and swap
CAS操作本身必须得是原子性的才可以。
比如:
if( 变量==原先 ){
修改变量的值; //进行到这一步的时候,有一个线程把值改成了8,但已经过了判断,此时就只能把8改成1了。
}
【 Atomic类深入认识CAS 】:
/**
* 解决同样的问题的更高效的方法,使用AtomXXX类
* AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的
*/
package T05_YuanZiXing;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class T00_03_AtomicInteger {
/*volatile*/ //int count1 = 0;
AtomicInteger count = new AtomicInteger(0);
/* synchronized */void m() {
for (int i = 0; i < 10000; i++)
//if count1.get() < 1000
count.incrementAndGet(); //count1++
}
public static void main(String[] args) {
T00_03_AtomicInteger t = new T00_03_AtomicInteger();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 100; i++) {
threads.add(new Thread(t::m, "thread-" + i));
}
threads.forEach((o) -> o.start());
threads.forEach((o) -> {
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(t.count);
}
}
【最终输出】:
【Hotspot理解CAS】:
atomic这个类上的Linux实现 , X86架构下的 。
【94行】:
首先判断是不是MP , Multi-Processors , 你是不是多核的处理器 ,如果你是MP的话,前面加一条LOCK指令 ;
后面的汇编指令是cmpxchgl , CPU在底层的时候支持汇编指令cmpxchgl 。
最终实现:
cmpxchg = cas修改变量值
lock cmpxchg 指令
硬件:
lock指令在执行的时候视情况采用缓存锁或者总线锁。
【最终总结】:
如果你是多核的——加lock ;
不是多核的——直接cmpxchg指令即可。
【原子与否】:
cmpxchg指令这个不是原子的。———《汇编语言操作手册》
【得出的结论】:
CAS操作得益于CPU在汇编指令操作上支持cmpxchg指令。多核的话要在cmpxchg指令前加Lock指令。CAS底层还是使用了悲观锁,虽然叫“乐观锁”。
【 乐观锁 和 悲观锁 的效率比较 】:
【按使用场景比较】:
不同的场景:
临界区执行时间比较长 , 等的人很多 -> 重量级
时间短,等的人少 -> 自旋锁
【实现】:
和悲观锁关联的往往有一个队列;线程在队列里等着。——在队列里等着的那些线程是不消耗CPU资源的。
乐观锁是一圈一圈的自旋:
//一圈一圈的循环( 和原值进行比较 ),是需要消耗CPU资源的。
【CPU资源】:
乐观锁消耗的CPU资源更多一些。
【如何选择】:
执行时间长 , 排队的人又多————悲观锁。
执行时间短, 排队等待的人少—-——乐观锁。
【追求量化的话】:
自己去压力测试。
【实战选择】:
实战就用synchronized即可~
synchronized做了一系列的优化 , 在它内部既有自旋锁 ,偏向锁 , 重量级锁进行自适应的升级过程,它的效率已经升级的很不错了,自动完成锁升级过程。
【synchronized和三大特性 】:
【synchronized保障可见性】: