`
wupuyuan
  • 浏览: 77444 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java之锁研究

阅读更多
Lock和synchronized

    JDK1.5以后,在锁机制方面引入了新的锁-Lock,在网上的说法都比较笼统,结合网上的信息和我的理解这里做个总结。

    java现有的锁机制有两种实现方式,J.DK1.4前是通过synchronized实现,JDK1.5后加入java.util.concurrent.locks包下的各种lock(以下简称Lock)

    先说说代码层的区别。
    synchronized:在代码里,synchronized类似“面向对象”,修饰类、方法、对象。
    Lock:不作为修饰,类似“面向过程”,在方法中需要锁的时候lock,在结束的时候unlock(一般都在finally块里)。
例如代码:

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好的。

    最后还是希望各位拍砖。
4
3
分享到:
评论
2 楼 wupuyuan 2011-08-31  
xieyuNL 写道
不错,对于加锁方面的内容如果有图形讲解和形象的例子就更好了,因为这些东西本身较为抽象,很多人看了估计是云里雾里的感觉。

这个……这里图形上传还不熟……多谢来捧场,哈哈
1 楼 xieyuNL 2011-08-30  
不错,对于加锁方面的内容如果有图形讲解和形象的例子就更好了,因为这些东西本身较为抽象,很多人看了估计是云里雾里的感觉。

相关推荐

    基于java的锁具装箱的源程序代码

    Java中的锁装箱是并发编程中的一个重要概念,它涉及到Java的并发工具类,特别是`java.util.concurrent.atomic`包中的原子类。锁装箱是将基本数据类型包装成对象,然后在这些对象上实现线程安全的操作,以提高多线程...

    java 虚拟机的研究

    类文件验证是Java虚拟机确保字节码安全性的关键步骤之一。验证过程主要分为以下几部分: - **魔数与版本号验证**:首先检查类文件的魔数是否为`CAFEBABE`,确认文件格式正确。随后检查主版本号和次版本号,以确保类...

    Java中瘦锁的应用

    这种锁的实现比传统锁更轻量,因此称之为“瘦锁”。在文档中提到的另一个关键点是锁算法的评估,这涉及到评估瘦锁与传统锁性能的对比,以及在不同场景下的适用性。 文档中还提到了一项研究,该研究使用了基准测试...

    各种锁汇总,乐观锁、悲观锁、分布式锁、可重入锁、互斥锁、读写锁、分段锁、类锁、行级锁等

    8. **类锁**:类锁是Java中类级别的锁,通过synchronized修饰类的静态方法或者同步代码块实现,确保同一时间只有一个类实例执行特定的代码。 9. **行级锁**:在数据库管理系统中,行级锁只锁定被操作的数据行,其他...

    Java主题研究

    Java主题研究,主要聚焦在Java的内核编程,这一领域涵盖了Java语言的基础、虚拟机机制、内存管理以及并发编程等多个核心知识点。Java作为一款广泛应用的编程语言,其强大的跨平台能力和高效的性能使得它在企业级应用...

    中山大学研究生学院java讲义之(多线程)

    综上所述,中山大学研究生学院的Java多线程课程涵盖了多线程编程的关键知识点,从基础到高级,为学生提供了全面的并发编程理论和实践指导。通过学习这些内容,学生可以更好地理解和解决实际开发中的并发问题。

    息屏下唤醒和解锁手机(java-js).rar

    通过研究这些内容,开发者可以学习如何将Java和JavaScript结合起来,为移动应用提供更丰富的交互体验。这包括理解Android系统的组件交互、权限管理和事件监听,以及JavaScript中模拟触控事件和提高Web应用性能的技巧...

    面向Java锁机制的字节码自动重构框架.pdf

    根据提供的文件内容,以下是关于“面向Java锁机制的字节码自动重构框架”的详细知识点解析: ...文档中列出的关键词包括Java锁、软件重构、程序分析和字节码转换,这些关键词准确地概括了文章的主题和研究内容。

    redis分布式锁工具包提供纯Java方式调用

    Redis 分布式锁工具包是Java开发者在处理高并发、多节点系统中实现锁机制的一种高效解决方案。这个工具包提供了一种纯Java的调用方式,使得开发者无需深入理解Redis命令,也能便捷地在应用程序中集成分布式锁功能。...

    Java占用内存的研究.pdf

    在本文档中,我们主要研究了Java中对象和数据结构的内存占用情况。Java语言因为其跨平台特性,在内存使用上有一套自己的规则。特别地,Java虚拟机(JVM)管理内存的方式使得开发者需要了解一些基本的内存占用知识,...

    Java最新2024学习路线+Java自学全套免费网盘资料

    - Java并发编程:掌握线程、锁、并发集合、线程池等并发机制。 - Spring框架:学习Spring的核心原理,包括IOC、AOP等。 - 微服务架构:了解微服务架构的设计理念,掌握Spring Cloud等框架的使用。 - 数据库技术:...

    高并发Java服务器设计研究.zip

    本资料“高并发Java服务器设计研究”深入探讨了如何有效地处理大量并发请求,以确保系统的稳定性和效率。以下将对这个主题展开详细的解析。 1. **并发基础** - 并发性是指系统在同一时间处理多个任务的能力。在...

    ~_~java之经典源码大全~_~

    通过深入研究这份“java之经典源码大全”,开发者不仅可以巩固基础,还能提升对Java高级特性的理解和应用能力。此外,源码阅读也有助于培养良好的编程习惯和代码风格,是成长为专业Java开发者的必经之路。

    基于硬件cache锁机制的Java虚拟机即时编译器优化

    本文的研究成果为Java虚拟机即时编译器在多任务实时系统中的应用提供了理论和技术支持,特别是在使用硬件cache锁机制进行性能优化方面。通过本文链接,读者可以进一步获取该研究成果的详细信息,以及作者详细的研究...

    Java单例模式应用研究.pdf

    ### Java单例模式应用研究 #### 一、单例模式概述 单例模式(Singleton Pattern)作为一种最基本的创建型设计模式,其主要目的是控制一个类的实例化过程,确保在整个应用程序中仅存在一个实例,并且该实例能够被全局...

    Java并发编程全景图.pdf

    Java并发编程是Java语言中最为复杂且重要的部分之一,它涉及了多线程编程、内存模型、同步机制等多个领域。为了深入理解Java并发编程,有必要了解其核心技术点和相关实现原理,以下将详细介绍文件中提及的关键知识点...

    (超赞)JAVA精华之--深入JAVA API

    **2.3 Java/J2EE中文问题终极解决之道** - **中文支持** - 处理 Java 应用程序中的中文问题。 **2.4 Java Web应用中的任务调度** - **任务调度** - 使用定时器或计划任务来执行周期性任务。 **2.5 用连接池...

Global site tag (gtag.js) - Google Analytics