Lock和synchronized
    JDK1.5以后,在锁机制方面引入了新的锁-Lock,在网上的说法都比较笼统,结合网上的信息和我的理解这里做个总结。
    java现有的锁机制有两种实现方式,J.DK1.4前是通过synchronized实现,JDK1.5后加入java.util.concurrent.locks包下的各种lock(以下简称Lock)
    先说说代码层的区别。     synchronized:在代码里,synchronized类似“面向对象”,修饰类、方法、对象。     Lock:不作为修饰,类似“面向过程”,在方法中需要锁的时候lock,在结束的时候unlock(一般都在finally块里)。 例如代码:

  1. public void method1() {
  2. synchronized(this){//旧锁,无需人工释放
  3. System.out.println(1);
  4. }
  5. }
  6. public void method2() {
  7. Lock lock = new ReentrantLock();
  8. lock.lock();//上锁
  9. try{
  10. System.out.println(2);
  11. }finally{
  12. lock.unlock();//解锁
  13. }
  14. }
public void method1() {
synchronized(this){//旧锁,无需人工释放
System.out.println(1);
}
} public void method2() {
Lock lock = new ReentrantLock();
lock.lock();//上锁
try{
System.out.println(2);
}finally{
lock.unlock();//解锁
}
}

其次说说性能。     相关的性能测试网上已经有很多,这里也直接拿来主义,给出结论:     在并发高是,luck性能优势很明显,在低并发时,synchronized也能取得优势。具体的临界范围比较难定论,下面会讨论。
    现在来分析它们具体的区别。     锁都是 原子性 的,也可以理解为锁是否在使用的标记,并且比较和设置这个标记的操作是原子性的,不同硬件平台上的jdk实现锁的相关方法都是native的(比如park/unpark),所以不同平台上锁的精确度的等级由这些native的方法决定。所以网上经常可以看见的结论是“Lock比synchronized有更精确的原子操作”说的也是native方法(不得不感慨C才是硬件王道)。
下面继续讨论怎么由代码层到native的过程。 1、所有对象都自动含有单一的锁,JVM负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数变为0。在任务(线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务(线程)在此对象上获得锁时,计数会递增。 只有首先获得锁的任务(线程)才能继续获取该对象上的多个锁。每当任务离开时,计数递减,当计数为0的时候,锁被完全释放。synchronized就是基于这个原理,同时synchronized靠某个对象的单一锁技术的次数来判断是否被锁,所以无需(也不能)人工干预锁的获取和释放。如果结合方法调用时的栈和框架(如果对方法的调用过程不熟悉建议看看http://wupuyuan.iteye.com/blog/1157548),不难推测出synchronized原理是基于栈中的某对象来控制一个框架,所以对于synchronized有常用的优化是锁对象不锁方法。实际上synchronized作用于方法时,锁住的是“this”,作用于静态方法/属性时,锁住的是存在于永久带的CLASS,相当于这个CLASS的全局锁,锁作用于一般对象时,锁住的是对应代码块。在HotSpot中JVM实现中,锁有个专门的名字:对象监视器。
当多个线程同时请求某个对象监视器时,对象监视器会设置几种状态用来区分请求的线程 Contention List:所有请求锁的线程将被首先放置到该竞争队列,是个虚拟队列,不是实际的Queue的数据结构。 Entry List:EntryList与ContentionList逻辑上同属等待队列,ContentionList会被线程并发访问,为了降低对ContentionList队尾的争用,而建立EntryList。,Contention List中那些有资格成为候选人的线程被移到Entry List  Wait Set:那些调用wait方法被阻塞的线程被放置到Wait Set OnDeck:任何时刻最多只能有一个线程正在竞争锁,该线程称为OnDeck Owner:获得锁的线程称为Owner !Owner:释放锁的线程
2、Lock不同于synchronized面向对象,它基于栈中的框架而不是某个具体对象,所以Lock只需要在栈里设置锁的开始和结束(lock和unlock)的地方就行了(人工必须标明),不用关心框架大小对象的变化等等。这么做的好处是Lock能提供无条件的、可轮询的、定时的、可中断的锁获取操作,相对于synchronized来说,synchronized的锁的获取是释放必须在一个模块里,获取和释放的顺序必须相反,而Lock则可以在不同范围内获取释放,并且顺序无关。java.util.concurrent.locks下的锁类很类似,依赖于java.util.concurrent.AbstractQueuedSynchronizer,它们把所有的Lock接口操作都转嫁到Sync类上,这个类继承了AbstractQueuedSynchronizer,它同时还包含子2个类:NonfairSync 和FairSync 从名字上可以看的出是为了实现公平和非公平性。AbstractQueuedSynchronizer中把所有的的请求线程构成一个队列(一样也是虚拟的),具体的实现可以参考http://blog.csdn.net/chen77716/article/details/6641477#,这里我就不复制了。
3、从jdk的源代码来看,Lock和synchronized的源码基本相同,区别主要在维护的同步队列上。再往下深究就到了native方法了。
4、还有个改进我也想说下,其实很重要的。线程分阻塞(wait)和非阻塞状态,阻塞状态由操作系统(linux、windows等)完成,当前一个被“锁”的线程执行完毕后,有可能在后续的线程队列里还没分配出一个获取锁而被“唤醒”的非阻塞线程,即所有线程还都是阻塞状态时,就被系统调度(进入内核的线程是阻塞的),这样会导致内核在用户态和内核态之间来回接换,严重影响锁的性能。在jdk1.6以前主要靠自旋锁来解决,原理是在前一个线程结束后,争用线程可以做一个空循环,继续占有CPU,等待取锁的机会。当然这样做显然也是浪费时间,只是在两种浪费中选取浪费少的……  jdk1.6后引入了偏向锁,当线程第一次获得了监视对象,之后让监视对象“偏向”这个线程,之后的多次调用则可以避免CAS操作,等于是置了一临时变量来记录位置(类似索引比较)。详细的就涉及到汇编指令了,我也就没太深究,偏向锁性能优于自旋锁,但是还是没有达到HotSpot认为的最佳时间(一个线程上下文切换的时间)。
    综合来看对于所有的高并发情况,采用Lock加锁是最优选择,但是由于历史遗留等问题,synchronized也还是不能完全被淘汰,同时,在低并发情况下,synchronized的性能还是比Lock好的。

Java的锁研究的更多相关文章

  1. 深入理解JVM(③)Java的锁优化

    前言 从JDK5到JDK6HotSpot虚拟机开发团队花费了大量的资源实现了各种锁优化技术,如适应性自旋(Adaptive Spinning).锁消除(Lock Elimination).锁膨胀(Lo ...

  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偏向锁实现原理(Biased Locking)

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

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

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

  8. Java的锁

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

  9. Java类锁和对象锁

    一.类锁和对象锁 二.使用注意 三.参考资料 一.类锁和对象锁 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class) 对象锁:在代码 ...

随机推荐

  1. Item 4 ----通过私有构造器强化不可实例化的能力

    场景: 在创建工具类的时候,大部分是无需实例化的,实例化对它们没有意义.在这种情况下,创建的类,要确保它是不可以实例化的.   存在问题: 在创建不可实例化的类时,虽然没有定义构造器.但是,客户端在使 ...

  2. Vue 使用中的小技巧(山东数漫江湖)

    在vue的使用过程中会遇到各种场景,当普通使用时觉得没什么,但是或许优化一下可以更高效更优美的进行开发.下面有一些我在日常开发的时候用到的小技巧,在下将不定期更新~ 1. 多图表resize事件去中心 ...

  3. java springmvc4 图片或文件上传

    1.文件配置 配置文件解析 上传文件处理的核心方法 // uploadOneFile.jsp, uploadMultiFile.jsp submit to. @RequestMapping(value ...

  4. rabbitmq之配置文件详解(二)

    前言 前面介绍了erlang环境的安装和rabbitmq环境安装,接下来对rabbitmq详细配置: 设置配置文件 rabbitmq的系统配置文件一般是rabbitmq.conf,可以登录后台查看它的 ...

  5. Linux线程同步

    1. 线程同步: 当多个控制线程共享相同的内存时,需要确保每个线程看到一致的数据视图.当某个线程可以修改变量,而其他线程也可以读取或者修改这个变量的时候,就需要对这些线程进行同步,以确保他们在访问变量 ...

  6. ftrace 简介【转】

    转自:http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace/index.html Trace 对于软件的维护和性能分析至关重要,ftrace 是 ...

  7. mongodb 学习笔记 2 --- 修改器

    修改器是为了爱update文档时,不需要传入整个文档就能修改当前文档的某个属性值,修改器用法如下: 假设数据库中foo集合中存在如下文档:{"name":"jack&qu ...

  8. abp 调试

    概要 研究Abp(ASP.NET Boilerplate)框架有几个月了,从一遍遍的看官方文档,到现在看源码,一路走来学习了很多知识. 很多新手都很关心源码如何调试,我也是如此,在反复看Debuggi ...

  9. PHP下载APK文件

    PHP下载APK文件(代码如下) /** * //这里不要随便打印文字,否则会影响输出的文件的 * (例如下载没问题,但是apk安装时候提醒解析安装包错误) * @return array */ pu ...

  10. goreplay HTTP-HTTPS流量复制工具

    goreplay相比tcpcopy只能复制HTTP和HTTPS的流量 goreplay编译很麻烦,就直接使用编译好的版本 gor_0.10.1_x64.tar.gz 支持centos5,测试的是cen ...