锁是java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。
参见http://blog.csdn.net/fw0124/article/details/6671447
- 线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
- 线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做修改的)消息。
- 线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息。
使用公平锁时,加锁方法lock()的方法调用轨迹如下:
- ReentrantLock : lock()
- FairSync : lock()
- AbstractQueuedSynchronizer : acquire(int arg)
- FairSync : tryAcquire(int acquires)
在第4步真正开始加锁
解锁方法unlock()的方法调用轨迹如下:
- ReentrantLock : unlock()
- AbstractQueuedSynchronizer : release(int arg)
- Sync : tryRelease(int releases)
在第3步真正开始释放锁
使用非公平锁时,加锁方法lock()的方法调用轨迹如下:
- ReentrantLock : lock()
- NonfairSync : lock()
- AbstractQueuedSynchronizer : compareAndSetState(int expect, int update)
在第3步真正开始加锁
解锁过程与公平锁相同
公平锁和非公平锁的内存语义总结:
- 公平锁和非公平锁释放时,最后都要写一个volatile变量state。
- 公平锁获取时,首先会去读这个volatile变量。
- 非公平锁获取时,首先会用CAS更新这个volatile变量,这个操作同时具有volatile读和volatile写的内存语义。
锁释放-获取的内存语义的两种实现方式:
- 利用volatile变量的写-读所具有的内存语义。
- 利用CAS所附带的volatile读和volatile写的内存语义。
缓存锁定(cache locking):如果要访问的内存区域(area of memory)在lock前缀指令执行期间已经在处理器内部的缓存中被锁定(即包含该内存区域的缓存行当前处于独占或以修改状态),并且该内存区域被完全包含在单个缓存行(cache line)中,那么处理器将直接执行该指令。由于在指令执行期间该缓存行会一直被锁定,其它处理器无法读/写该指令要访问的内存区域,因此能保证指令执行的原子性。
Java线程之间的通信现在有了下面四种方式:
- A线程写volatile变量,随后B线程读这个volatile变量。
- A线程写volatile变量,随后B线程用CAS更新这个volatile变量。
- A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量。
- A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量。
concurrent包的源代码实现通用化实现模式:
- 首先,声明共享变量为volatile;
- 然后,使用CAS的原子条件更新来实现线程之间的同步;
- 同时,配合以volatile的读/写和CAS所具有的volatile读和写的内存语义来实现线程之间的通信。
ReentrantLock
1.
ReentrantLock
类实现了Lock
,它拥有与synchronized
相同的并发性和内存语义,但是添加了时间锁等候、可中断锁等候、无块结构锁、多个条件变量、锁投票等一些特性。此外,它还提供了在激烈争用情况下更佳的性能。具体语法参见http://blog.csdn.net/vernonzheng/article/details/82882512.有一个与锁相关的获取计数器,如果拥有锁的某个线程再次得到锁,那么获取计数器就加1,然后锁需要被释放两次才能获得真正释放
3.
Lock
和 synchronized 有一点明显的区别 —— lock 必须在 finally 块中释放。否则,如果受保护的代码将抛出异常,锁就有可能永远得不到释放4.当许多线程都在争用同一个锁时,使用
ReentrantLock
的总体开支通常要比synchronized
少得多5.同步内置的监控器锁永远都是不公平的,但JVM 保证了所有线程最终都会得到它们所等候的锁,确保统计上的公平性。
6.synchronized优势: 会自动释放锁;当 JVM 用 synchronized 管理锁定请求和释放时,JVM 在生成线程转储时能够包括锁定信息。
- A线程写volatile变量,随后B线程读这个volatile变量。
- A线程写volatile变量,随后B线程用CAS更新这个volatile变量。
- A线程用CAS更新一个volatile变量,随后B线程用CAS更新这个volatile变量。
- A线程用CAS更新一个volatile变量,随后B线程读这个volatile变量。