使用锁可以让临界区的代码互斥执行。锁是java并发编程中最重要的同步机制。
锁还可以让释放锁的线程向获取同一个锁的线程发送消息
-->锁的释放和获取 内存语义
1. 锁的释放:当释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存
2. 锁的获取:当获取锁时,JMM会把线程对应的本地内存置为无效,而从主内存中读取共享变量
上述共享变量的范围:实例字段、静态字段‘、数组对象元素
由此可见锁的释放与volatile写具有相同语义,锁的获取与volatile读具有相同语义。
-->锁的内存语义实现
代码级别:调用lock()方法获取锁;调用unlock()方法释放锁(依赖与AQS同步框架)
锁分为公平锁和非公平锁
公平锁在释放锁的最后写volatile变量state,在获取锁时首先读这个volatile变量。根据volatile的happens-before规则,释放锁的线程在写volatile变量之前可见的共享变量,在获取锁的线程读取同一个volatile变量后将立即变得对获取锁的线程可见。
(捂脸...别问我为什么没有非公平锁的解释,才疏学浅..看不懂。这个部分也在和实验室的同学激烈地讨论工作时间与通勤时间中度过)
公平锁与非公平锁
1. 公平锁和非公平锁释放时,最后都要写一个volatile变量state
2. 公平锁获取时,首先会去读volatile变量
3. 非公平锁获取锁时,首先会用CAS更新volatile变量,这个操作同时具有volatile读和写的内存语义
总结:锁释放-获取的内存语义的实现至少有下面两种方式:
1. 利用volatile变量的写-读所具有的内存语义;
2. 利用CAS所附带的volatile读和volatile写的内存语义。
Concurrent包的实现
java线程之间的通信有下面4种方式:
1. A线程写volatile变量,随后线程B读这个volatile变量。
2. A线程写volatile变量,随后B线程用CAS更新这个变量。
3. A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个变量。
4. A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量。
最后补充一下concurrent包的结构:
Lock 同步器 阻塞队列 Executor 并发容器
AQS 非阻塞数据结构 原子变量类
volatile变量的读/写 CAS
concurrent包的源代码实现是一个通用的模式。
首先,声明共享变量为volatile。
然后,使用CAS的原子条件更新来实现线程之间的同步。
同时,配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现进程间的通信。
AQS,非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),这些concurrent包中的基础类都是使用这种模式来实现的,而concurrent包中的高层类又是依赖于这些基础类实现