锁是java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可让释放锁的线程向获取同一个锁的线程发送消息。html
当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中;
当线程获取锁时,JMM会当前线程拥有的本地内存共享变量置为无效,从而使得被监视器保护的临界区代码必需要从主内存中去读取共享变量;java
CAS是单词compare and set的缩写,意思是指在set以前先比较该值有没有变化,只有在没变的状况下才对其赋值。编程
问题:如何在没有锁的状况下实现i++原子操做?缓存
CAS操做涉及到三个操做数,一个是内存值,一个是旧的预期值,一个是更新后的值,若是内存值和旧的预期值没有发生变化,才设置成新的值。多线程
public final int incrementAndGet() { for (;;) { //获得预期值 int current = get(); //获得更新后的值 int next = current + 1; //经过CAS操做验证是否发生变化 if (compareAndSet(current, next)) return next; } }
CAS的原子性其实是CPU实现的.并发
CAS操做用途:能够用CAS在无锁的状况下实现原子操做,但要明确应用场合,很是简单的操做且又不想引入锁能够考虑使用CAS操做,当想要非阻塞地完成某一操做也能够考虑CAS。不推荐在复杂操做中引入CAS,会使程序可读性变差,且难以测试,同时会出现ABA问题。性能
2.1 volatile关键字的特性:测试
(1)可见性:对一个volatile关键字的读,老是能看到(任意线程)对这个关键字的写 (2)原子性:对任意单个volatile变量的写操做,具备原子性(注:多个volatile组合操做不具备原子性)
2.2 内存语义优化
2.3 实现原理操作系统
instance = new Singleton(); 定义一个volatile变量
其对应编译后的cpu指令为:
0x01a3de1d: movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);
由编译后的汇编指令能够看出,改指令相比其余指令多个一个lock前缀
Lock前缀的指令在多核处理器下会引起了两件事情:
具体实现细节:
锁的内存语义的实现与可重入锁相关,能够简要总结锁的内存语义的实现包括如下两种方式:
synchronized也称为监视器锁,由JVM控制实现,每一个对象都有相似监视器同样的锁,当监视器锁被占用是对象将会处于锁定状态, 每一个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的全部权,过程以下:
当线程执行monitorexit指令时候,过程以下:
执行monitorexit的线程必须是objectref所对应的monitor的全部者。
指令执行时,monitor的进入数减1,若是减1后进入数为0,那线程退出monitor,再也不是这个monitor的全部者。其余被这个monitor阻塞的线程能够尝试去获取这个 monitor 的全部权。
经过这两段描述,咱们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是经过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为何只有在同步的块或者方法中才能调用wait/notify等方法,不然会抛出java.lang.IllegalMonitorStateException的异常的缘由。
加轻量锁的过程很简单:在当前线程的栈帧(stack frame)中生成一个锁记录(lock record),这个锁记录比前面说的那个对象锁(管理线程队列的monitor)简单多了,它只是对象头的一个拷贝。而后把对象头里的tag改为00,并把这个栈帧里的lock record地址放入对象头里。若操做成功,那就完成了轻量锁操做。若是不成功,说明有线程在竞争,则须要在当前对象上生成重量锁来进行多线程同步,而后将Tag状态改成10,并生成Monitor对象(重量锁对象),对象头里也会放入Monitor对象的地址。最后将当前线程t排队队列中。
轻量锁的解锁过程也很简单就是把栈帧里刚才的那个lock record拷贝到对象头里,若替换成功,则解锁完成,若替换不成功,表示在当前线程持有锁的这段时间内,其余线程也竞争过锁,而且发生了锁升级为重量锁,这时须要去Monitor的等待队列中唤醒一个线程去从新竞争锁。
1. 二者都是可重入的锁; 2. synchronized就不是可中断锁,而Lock是可中断锁; 3. synchronized是非公平锁,而lock提供公平锁的实现; 4. Lock提供读写两种锁操做;
性能比较:
在JDK1.5中,synchronized的性能是比较低的,线程阻塞和唤醒由操做系统内核完成,频繁的加锁和放锁致使频繁的上下文切换,形成效率低下;所以在多线程环境下,synchronized的吞吐量降低的很是严重。但在JDK1.6时对synchronized进行了不少优化,包括偏向锁、自适应自旋、轻量级锁等措施。
当须要如下高级特性时,才应该使用Lock:可定时的、可轮询的与可中断的锁获取操做,公平队列,或者非块结构的锁。不然,请使用synchronized。
https://www.cnblogs.com/pony1223/p/9428248.html