在 JDK 1.6 以前,synchronized 是重量级锁,效率低下。html
从 JDK 1.6 开始,synchronized 作了不少优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减小锁操做的开销。java
synchronized 同步锁一共包含四种状态:无锁、偏向锁、轻量级锁、重量级锁,它会随着竞争状况逐渐升级。synchronized 同步锁能够升级可是不能够降级,目的是为了提升获取锁和释放锁的效率。git
经过反编译.class文件,经过查看字节码能够获得:在代码块中使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令指明同步代码块的结束位置。github
一样查看字节码能够获得:在同步方法中会包含 ACC_SYNCHRONIZED 标记符。该标记符指明了该方法是一个同步方法,从而执行相应的同步调用。算法
对象锁:使用 synchronized 修饰非静态的方法以及 synchronized(this) 同步代码块使用的锁是对象锁。编程
类锁:使用 synchronized 修饰静态的方法以及 synchronized(class) 同步代码块使用的锁是类锁。并发
私有锁:在类内部声明一个私有属性如private Object lock,在须要加锁的同步块使用 synchronized(lock)框架
它们的特性:分布式
由私有锁实现的等待/通知机制:工具
Object lock = new Object();
// 由等待方线程实现
synchronized (lock) {
while (条件不知足) {
lock.wait();
}
}
// 由通知方线程实现
synchronized (lock) {
条件发生改变
lock.notify();
}
复制代码
ReentrantLock 是一个独占/排他锁。相对于 synchronized,它更加灵活。可是须要本身写出加锁和解锁的过程。它的灵活性在于它拥有不少特性。
ReentrantLock 须要显示地进行释放锁。特别是在程序异常时,synchronized 会自动释放锁,而 ReentrantLock 并不会自动释放锁,因此必须在 finally 中进行释放锁。
它的特性:
ReentrantLock 是不少类的基础,例如 ConcurrentHashMap 内部使用的 Segment 就是继承 ReentrantLock,CopyOnWriteArrayList 也使用了 ReentrantLock。
我以前写过一篇文章《ReentrantReadWriteLock读写锁及其在 RxCache 中的使用》 曾详细介绍过ReentrantReadWriteLock。
它拥有读锁(ReadLock)和写锁(WriteLock),读锁是一个共享锁,写锁是一个排他锁。
它的特性:
上面提到的 ReentrantLock、ReentrantReadWriteLock 都是基于 AbstractQueuedSynchronizer (AQS),而 AQS 又是基于 CAS。CAS 的全称是 Compare And Swap(比较与交换),它是一种无锁算法。
synchronized、Lock 都采用了悲观锁的机制,而 CAS 是一种乐观锁的实现。
CAS 的特性:
CAS 存在的问题:
Condition 用于替代传统的 Object 的 wait()、notify() 实现线程间的协做。
在 Condition 对象中,与 wait、notify、notifyAll 方法对应的分别是 await、signal 和 signalAll。
Condition 必需要配合 Lock 一块儿使用,一个 Condition 的实例必须与一个 Lock 绑定。
它的特性:
Semaphore、CountDownLatch、CyclicBarrier 都是并发工具类。
Semaphore 能够指定多个线程同时访问某个资源,而 synchronized 和 ReentrantLock 都是一次只容许一个线程访问某个资源。因为 Semaphore 适用于限制访问某些资源的线程数目,所以可使用它来作限流。
Semaphore 并不会实现数据的同步,数据的同步仍是须要使用 synchronized、Lock 等实现。
它的特性:
CountDownLatch 能够当作是一个倒计数器,它容许一个或多个线程等待其余线程完成操做。所以,CountDownLatch 是共享锁。
CountDownLatch 的 countDown() 方法将计数器减1,await() 方法会阻塞当前线程直到计数器变为0。
在个人爬虫框架NetDiscovery中就使用了 CountDownLatch 来控制某个爬虫的暂停和恢复。
本文小结了 Java 经常使用的一些锁及其一些特性,掌握这些锁是掌握 Java 并发编程的基础。固然,Java 的锁并不止这些,例如 ConcurrentHashMap 的分段锁(Segment),分布式环境下所使用的分布式锁。
参考资料:
Java与Android技术栈:每周更新推送原创技术文章,欢迎扫描下方的公众号二维码并关注,期待与您的共同成长和进步。