一. synchronized

  在 JDK 1.6 之前,synchronized 是重量级锁,效率低下。

  从 JDK 1.6 开始,synchronized 做了很多优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。

  synchronized 同步锁一共包含四种状态:无锁、偏向锁、轻量级锁、重量级锁,它会随着竞争情况逐渐升级。synchronized 同步锁可以升级但是不可以降级,目的是为了提高获取锁和释放锁的效率。

  synchronized 的底层原理

  synchronized 修饰的代码块

  通过反编译.class文件,通过查看字节码可以得到:在代码块中使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令指明同步代码块的结束位置。

  synchronized 修饰的方法

  同样查看字节码可以得到:在同步方法中会包含 ACC_SYNCHRONIZED 标记符。该标记符指明了该方法是一个同步方法,从而执行相应的同步调用。

  二. 对象锁、类锁、私有锁

  对象锁:使用 synchronized 修饰非静态的方法以及 synchronized(this) 同步代码块使用的锁是对象锁。

  类锁:使用 synchronized 修饰静态的方法以及 synchronized(class) 同步代码块使用的锁是类锁。

  私有锁:在类内部声明一个私有属性如private Object lock,在需要加锁的同步块使用 synchronized(lock)

  它们的特性:

  对象锁具有可重入性。

  当一个线程获得了某个对象的对象锁,则该线程仍然可以调用其他任何需要该对象锁的 synchronized 方法或 synchronized(this) 同步代码块。

  当一个线程访问某个对象的一个 synchronized(this) 同步代码块时,其他线程对该对象中所有其它 synchronized(this) 同步代码块的访问将被阻塞,因为访问的是同一个对象锁。

  每个类只有一个类锁,但是类可以实例化成对象,因此每一个对象对应一个对象锁。

  类锁和对象锁不会产生竞争。

  私有锁和对象锁也不会产生竞争。

  使用私有锁可以减小锁的细粒度,减少由锁产生的开销。

  由私有锁实现的等待/通知机制:

  Object lock = new Object();

  // 由等待方线程实现

  synchronized (lock) {

  while (条件不满足) {

  lock.wait();

  }

  }

  // 由通知方线程实现

  synchronized (lock) {

  条件发生改变

  lock.notify();

  }

  三. ReentrantLock

  ReentrantLock 是一个独占/排他锁。相对于 synchronized,它更加灵活。但是需要自己写出加锁和解锁的过程。它的灵活性在于它拥有很多特性。

  ReentrantLock 需要显示地进行释放锁。特别是在程序异常时,synchronized 会自动释放锁,而 ReentrantLock 并不会自动释放锁,所以必须在 finally 中进行释放锁。

  它的特性:

  公平性:支持公平锁和非公平锁。默认使用了非公平锁。

  可重入

  可中断:相对于 synchronized,它是可中断的锁,能够对中断作出响应。

  超时机制:超时后不能获得锁,因此不会造成死锁。

  ReentrantLock 是很多类的基础,例如 ConcurrentHashMap 内部使用的 Segment 就是继承 ReentrantLock,CopyOnWriteArrayList 也使用了 ReentrantLock。

  四. ReentrantReadWriteLock

  我之前写过一篇文章《ReentrantReadWriteLock读写锁及其在 RxCache 中的使用》 曾详细介绍过ReentrantReadWriteLock。

  它拥有读锁(ReadLock)和写锁(WriteLock),读锁是一个共享锁,写锁是一个排他锁。

  它的特性:

  公平性:支持公平锁和非公平锁。默认使用了非公平锁。

  可重入:读线程在获取读锁之后能够再次获取读锁。写线程在获取写锁之后能够再次获取写锁,同时也可以获取读锁(锁降级)。

  锁降级:先获取写锁,再获取读锁,然后再释放写锁的过程。锁降级是为了保证数据的可见性。

  五. CAS

  上面提到的 ReentrantLock、ReentrantReadWriteLock 都是基于 AbstractQueuedSynchronizer (AQS),而 AQS 又是基于 CAS。CAS 的全称是 Compare And Swap(比较与交换),它是一种无锁算法。

  synchronized、Lock 都采用了悲观锁的机制,而 CAS 是一种乐观锁的实现。

  CAS 的特性:

  通过调用 JNI 的代码实现

  非阻塞算法

  非独占锁

  CAS 存在的问题:

  ABA

  循环时间长开销大

  只能保证一个共享变量的原子操作

  六. Condition

  Condition 用于替代传统的 Object 的 wait()、notify() 实现线程间的协作。

  在 Condition 对象中,与 wait、notify、notifyAll 方法对应的分别是 await、signal 和 signalAll。

  Condition 必须要配合 Lock 一起使用,一个 Condition 的实例必须与一个 Lock 绑定。

  它的特性:

  一个 Lock 对象可以创建多个 Condition 实例,所以可以支持多个等待队列。

  Condition 在使用 await、signal 或 signalAll 方法时,必须先获得 Lock 的 lock()

  支持响应中断

  支持的定时唤醒功能

  七. Semaphore

  Semaphore、CountDownLatch、CyclicBarrier 都是并发工具类。

  Semaphore 可以指定多个线程同时访问某个资源,而 synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源。由于 Semaphore 适用于限制访问某些资源的线程数目,因此可以使用它来做限流。

  Semaphore 并不会实现数据的同步,数据的同步还是需要使用 synchronized、Lock 等实现。

  它的特性:

  基于 AQS 的共享模式

  公平性:支持公平模式和非公平模式。默认使用了非公平模式。

  八. CountDownLatch

  CountDownLatch 可以看成是一个倒计数器,它允许一个或多个线程等待其他线程完成操作。因此,CountDownLatch 是共享锁。

  CountDownLatch 的 countDown() 方法将计数器减1,await() 方法会阻塞当前线程直到计数器变为0。

  在我的爬虫框架NetDiscovery中就使用了 CountDownLatch 来控制某个爬虫的暂停和恢复。

  九. 锁的分类

  

  十. 总结

  本文小结了 Java 常用的一些锁及其一些特性,掌握这些锁是掌握 Java 并发编程的基础。当然,Java 的锁并不止这些,例如 ConcurrentHashMap 的分段锁(Segment),分布式环境下所使用的分布式锁。

Java 各种锁的小结的更多相关文章

  1. 并发王者课-铂金1:探本溯源-为何说Lock接口是Java中锁的基础

    欢迎来到<并发王者课>,本文是该系列文章中的第14篇. 在黄金系列中,我们介绍了并发中一些问题,比如死锁.活锁.线程饥饿等问题.在并发编程中,这些问题无疑都是需要解决的.所以,在铂金系列文 ...

  2. java的锁机制

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线 ...

  3. JAVA线程锁-读写锁

    JAVA线程锁,除Lock的传统锁,又有两种特殊锁,叫读写锁ReadWriteLock 其中多个读锁不互斥,读锁和写锁互斥,写锁和写锁互斥 例子: /** * java线程锁分为读写锁 ReadWri ...

  4. Java线程锁一个简单Lock

    /** * @author * * Lock 是java.util.concurrent.locks下提供的java线程锁,作用跟synchronized类似, * 单是比它更加面向对象,两个线程执行 ...

  5. paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象)

    paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象) 1     锁的缺点 2     CAS(Compare ...

  6. Java内部类的使用小结

    转载:http://android.blog.51cto.com/268543/384844/ Java内部类的使用小结 内部类是指在一个外部类的内部再定义一个类.类名不需要和文件夹相同. *内部类可 ...

  7. Java偏向锁实现原理(Biased Locking)

    http://kenwublog.com/theory-of-java-biased-locking 阅读本文的读者,需要对Java轻量级锁有一定的了解,知道lock record, mark wor ...

  8. Java分布式锁之数据库实现

    之前的文章<Java分布式锁实现>中列举了分布式锁的3种实现方式,分别是基于数据库实现,基于缓存实现和基于zookeeper实现.三种实现方式各有可取之处,本篇文章就详细讲解一下Java分 ...

  9. Java的锁

    今天练习了Java的多线程,提到多线程就基本就会用到锁 Java通过关键字及几个类实现了锁的机制,这里先介绍下Java都有哪些锁:   一.Java实现锁的机制: Java运行到包含锁的代码时,获取尝 ...

随机推荐

  1. FineReport----单元格元素(数据列、公式、斜线)

    一.绑定.插入数据列 数据集字段绑定 二.公式 1.单元 格计算 1.两个单元格(C7,I7)和 2.等于某单元格的值 D7等C7单元格的值 3.C7/8(除) 4.除数被除数为零.空的处理 http ...

  2. PHP数组遍历详解

    一.PHP数组简介 1.PHP数组的分类 按照下标的不同分为关联数组和索引数组①索引数组:下标从0开始依次增长②关联数组:下标为字符串格式,每个下标字符串与数组的值一一对应,(有点像对象的键值对) 下 ...

  3. caffe杂

    一.finetune命令: mpirun /home/zhangsuosheng/caffe_mpi/build/tools/caffe train -solver solver.prototxt - ...

  4. Verilog学习总结

    1.多个always语句不能对同一变量赋值. 2.assign语句只能进行阻塞赋值,用来描述组合逻辑. 3.verilog描述方式:结构描述(门级描述和模块调用).数据流描述(assign,wire型 ...

  5. Ecstore 会员中心 菜单添加一项

    1.会员中心 添加菜单 ecstore_合并支付总结_会员中心添加菜单_20160113 class : b2c_ctl_site_member (图 1)     第一步: (图1)         ...

  6. VS2015配置安卓Android和iOS开发环境

    http://www.cjjjs.cn/paper/gzsh/627201502818357.aspx [摘要] 本文按照步骤一步步的介绍要下载安装的东西,都提供了下载地址.最后将所有需要的程序都打包 ...

  7. Java并发(1):synchronized

    虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就来一起讨论下线程安全问题,以及Java中提供 ...

  8. 杭电1025Constructing Roads In JGShining's Kingdom

    地址:http://acm.hdu.edu.cn/showproblem.php?pid=1025 题目: Problem Description JGShining's kingdom consis ...

  9. 【Java Web】新手教程(转)

    转自:http://www.journaldev.com/1854/java-web-application-tutorial-for-beginners#web-server-client Web ...

  10. article嵌套

    例如: 为了将每一个部分都突出显示,可以将article添加边框和颜色的属性: <style> article{ border: 1px solid #ff0000; margin: 5p ...