0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

JAVA最好的加锁方法是什么

汽车玩家 来源:oschina 作者:oschina 2020-05-03 17:44 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

关于synchronized

众所周知,JAVA中最简单的加锁方法是用关键字synchronized,我们可以使用这个关键字将一个方法变成线程安全的,也可以将一个代码块变成线程安全的,这样子我们不需要再担心多线程同时执行到这段代码会引发的并发问题。同时配合方法wait,notify和notifyall可以很好的实现多线程之间的协作,比如某个线程因为需要等待一些资源,于是调用wait方法将自己设置为waiting状态,其他线程释放或生产这个线程需要的资源的时候需要通知这个线程(notify)将其唤醒,或者通知所有等待当前资源的线程(notifyall)。

然而当功能完成之后我们似乎并不满足于此,于是我们开始考虑这么做的代价是什么,是否可以做的更好。

先说说这么做(使用synchronized)的代价是什么,当多个线程请求临界资源的时候只能有一个线程得到满足,那么其他的线程会做什么呢,他们会被阻塞,直到被通知(notify/notifyall)又有资源的时候才被唤醒进行再一次的锁争用,而后往复的是又只有一个线程能被得到满足,其他的线程继续进入阻塞状态,而这个时候可能会有不断的增加争用线程。性能损耗的关键点在于线程的阻塞操作是由操作系统来完成的,在Linux系统下是由pthread_mutex_lock函数来完成。线程被阻塞之后便进入了内核调度态,这个过程发生了操作系统将保存用户态的上下文进入内核态,这也就是常说的上下文切换,上下文切换代价大,在于操作系统需要将当前线程执行上下文内容(包括堆栈、寄存器等存储的内容)的保存以便之后线程切换回来时候再进行现场恢复。

上面可以看出使用synchronized的代价是什么了吧,当竞争激烈的时候会引起频繁的操作系统上下文切换,从而影响系统的性能。下面再来讲讲自旋锁。

自旋锁的原理

自旋锁是对线程阻塞的一种优化,他的原理简单的说就是当线程争用锁失败的时候不立即进入阻塞状态,而是再等一会,因为对于执行时间短的代码这一会可能就会释放锁,而线程就不需要进行一次阻塞与唤醒。等待操作就是让线程多执行几个空指令,至于等待多久这跟具体的处理器实现有关,也有可能处理器根本不支持自旋锁,具体实现的时候我们可以设置一个临界值,当超过了这个临界值之后我们就不自旋了,就乖乖进入阻塞状态吧。这种优化对于执行时间短的代码是很有效的。synchronized使用自旋锁的时机是线程进入等待队列即阻塞的前一步。

关于偏向锁

偏向锁是java6提供的一种功能,主要是对无竞争条件下的对加锁代码执行的优化,得到优化的地方是省去了对等待队列的更新操作。在竞争条件下,获取锁失败的线程会被放入等待队列,这个队列的更新操作是通过CAS指令来完成的。对于那么一段本部应该被加锁的代码被加了锁,我们认为每次执行这段被加了锁的代码的时候更新等待队列的操作并不是必要的,而CAS操作会延迟本地代码的执行,因此偏向锁是用于优化这个问题的。

关于Lock

Lock是JAVA5增加的内容,在JCU(java.util.concurrent.locks)包下面,作者是并发大师Doug Lea。JCU包提供了很多封装的锁,包括常用的ReentrantLock和ReadWriteLock。这些所其实都是依赖java.util.concurrent.AbstractQueuedSynchronizer这个类来实现的,这个类有个简写的名字叫AQS,对这就是著名的AQS。

关于Lock,先说说线程获取Lock锁的时候会引起哪些事件呢。首先AQS是依赖一个被volatile修饰的int变量来标识当前锁的状态的,为0的时候代表当前锁不被任何线程拥有,当线程拿到这个锁的时候会通过CAS操作修改state的状态,那么对于争用失败的线程AQS会怎么办呢,AQS内部维护了一个等待队列,这个队列是纯JAVA实现的,其实现也是非常巧妙的,多线程在通过CAS来获取自己在队列中的位置,同时队列中的线程状态也是阻塞状态,遇到阻塞就头疼了,上面已经介绍过阻塞会带来的性能问题。在源码中我们可以看到的是AQS通过LockSupport(LockSupport底层依赖Unsafe)将线程阻塞,关于LockSupport我有一篇文章介绍的,其功能是用来代替wait和notity/notifyall的,更好的地方是LockSupport对park方法和unpark方法的调用没有先后的限制,而notify/notifyall必须在wait调用之后调用。尽管如此,这一切并没有阻止线程进入阻塞状态,我有点失望。

无锁时代

讲到无锁,必然是Disruptor并发框架,Disruptor底层依赖一个RingBuffer来进行线程之间的数据交换,无锁在于在并发条件下,多线程对RingBuffer的读和写不会涉及到锁,然而因为RingBuffer满或者RingBuffer中没有可消费内容引发的线程等待,那就要另当别论了。简单几句介绍下无锁原理,RingBuffer维护者可读和可写的指针,也叫游标,它指向生产者或消费者需要写或读的位置,而对于指针的更新是由CAS来完成的,这个过程中我们不需要加锁/解锁的过程。

后记:

JAVA锁方面的知识主要是要搞清楚不同的锁的优点与缺点,深入到操作系统层的实现机制与不同场景中对应用的性能影响。本文简单的撸了一下JAVA锁从synchronized到无锁的发展以及一些锁的简单原理,主要是抛砖引玉吧,因为介绍的比较简单,对于文中提到的知识不知道的同学可以深入了解,我相信你会很有收获。有些实现的原理介绍可能就一句话,但是实际实现起来是蛮复杂的,需要考虑到的东西是我们没有写过所不能考虑到的。到这里,如果你的项目中用到了多线程并发,你是否会考虑使用无锁模型来优化你项目中多线程之间的通信呢。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 寄存器
    +关注

    关注

    31

    文章

    5589

    浏览量

    129066
  • JAVA
    +关注

    关注

    20

    文章

    2997

    浏览量

    115683
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Arm Neoverse CPU上大代码量Java应用的性能测试

    Java 是互联网领域广泛使用的编程语言。Java 应用的一些特性使其性能表现与提前编译的原生应用(例如 C 程序)大相径庭。由于 Java 字节码无法直接在 CPU 上执行,因此通常运行时在
    的头像 发表于 11-05 11:25 375次阅读
    Arm Neoverse CPU上大代码量<b class='flag-5'>Java</b>应用的性能测试

    Java 25正式发布,重要特性详解(附代码示例):灵活构造函数体、模块导入声明、AOT方法分析等

    Java 25现已发布,更多新特性来了!配合Perforce JRebel,代码修改即时生效,无需重启服务,即可实现“改完就看效果”。新特性+快工具,让你的Java开发体验双倍提升!
    的头像 发表于 10-29 13:16 855次阅读
    <b class='flag-5'>Java</b> 25正式发布,重要特性详解(附代码示例):灵活构造函数体、模块导入声明、AOT<b class='flag-5'>方法</b>分析等

    Java效率提升指南:5个Java工具选型建议及Perforce JRebel和XRebel介绍

    企业级Java环境越来越复杂,真正的破局点,可能不在“人”,而在于“工具”。5个实用建议,帮你理清Java工具的选型思路。
    的头像 发表于 09-11 13:59 908次阅读
    <b class='flag-5'>Java</b>效率提升指南:5个<b class='flag-5'>Java</b>工具选型建议及Perforce JRebel和XRebel介绍

    Java 在物联网与嵌入式系统中的应用前景与挑战

    引言 随着物联网与嵌入式技术的快速发展,设备端的软件开发需求不断增加。传统上,嵌入式设备多使用 C、C++ 进行开发,但近年来,Java 逐渐成为物联网领域的重要角色。凭借其跨平台特性、丰富
    的头像 发表于 09-04 14:49 644次阅读

    Perforce JRebel 简介:即时加载代码变更,加速Java应用开发

    Perforce JRebel 专为Java开发提速而生!支持跳过构建与重新部署,实时加载代码变更,支持100+框架,无缝集成主流IDE与应用服务器。
    的头像 发表于 08-14 14:35 488次阅读
    Perforce JRebel 简介:即时加载代码变更,加速<b class='flag-5'>Java</b>应用开发

    EtherCAT运动控制卡应用开发教程之Java

    运动控制卡的Java开发及DLL调用
    的头像 发表于 06-13 14:29 623次阅读
    EtherCAT运动控制卡应用开发教程之<b class='flag-5'>Java</b>

    ArkUI-X与Android桥接通信之方法回调

    ) => { console.error(\'error: \' + JSON.stringify(err)); }); 2.在Android侧实现被调用的方法。 // xxx.java
    发表于 06-08 22:16

    Java开发者必备的效率工具——Perforce JRebel是什么?为什么很多Java开发者在用?

    Perforce JRebel是一款Java开发效率工具,旨在帮助java开发人员更快地编写更好的应用程序。JRebel可即时重新加载对代码的修改,无需重启或重新部署应用程序,就能让开发者即时看到代码更改的效果,从而缩短开发、调试和测试周期,大大提升开发效率。
    的头像 发表于 04-27 13:44 648次阅读
    <b class='flag-5'>Java</b>开发者必备的效率工具——Perforce JRebel是什么?为什么很多<b class='flag-5'>Java</b>开发者在用?

    Java的SPI机制详解

    作者:京东物流 杨苇苇 1.SPI简介 SPI(Service Provicer Interface)是Java语言提供的一种接口发现机制,用来实现接口和接口实现的解耦。简单来说,就是系统只需要定义
    的头像 发表于 03-05 11:35 1111次阅读
    <b class='flag-5'>Java</b>的SPI机制详解

    java怎么控制DLP lcr4500投影仪LED开关?

    做的一款投影仪用到了DLP lcr4500,需要在自己的软件里添加控制投影仪LED开关的功能。软件是用JAVA写的,本人也没学过c++ 希望网友指点java怎么控制DLP lcr4500投影仪LED开关
    发表于 02-21 08:42

    Java应用OOM问题的排查过程

    导读 本文记录最近一例Java应用OOM问题的排查过程,希望可以给遇到类似问题的同学提供参考。 前言:此文记录最近一例Java应用OOM问题的排查过程,希望可以给遇到类似问题的同学提供参考。在本地
    的头像 发表于 02-12 11:15 1072次阅读
    <b class='flag-5'>Java</b>应用OOM问题的排查过程

    Spire.XLS for Android via Java组件说明

    Spire.XLS for Android via Java 是一款专业的 Android Excel 组件,用于在 Android 手机应用程序中创建、操作和转换 Excel 工作表,并且运行环境
    的头像 发表于 01-24 12:16 818次阅读
    Spire.XLS for Android via <b class='flag-5'>Java</b>组件说明

    华为云 Flexus X 实例下的场景体验——小企业必备——JAVA 环境搭建——保姆级教学

    前言 上次我们使用的是 Ubuntu 来操作的,这里跑的服务器多的还是 Huawei Cloud EulerOS,所以我们还原到基础镜像上做环境架设,此次我们来架设 java 的基础运行环境,是能
    的头像 发表于 01-07 17:05 743次阅读
    华为云 Flexus X 实例下的场景体验——小企业必备——<b class='flag-5'>JAVA</b> 环境搭建——保姆级教学

    校园点餐订餐外卖跑腿Java源码

    创建一个校园点餐订餐外卖跑腿系统是一个复杂的项目,涉及到前端、后端、数据库设计等多个方面。在这里,我可以提供一个简化的Java后端示例,使用Spring Boot框架来搭建一个基本的API服务。这个
    的头像 发表于 12-24 14:55 932次阅读
    校园点餐订餐外卖跑腿<b class='flag-5'>Java</b>源码

    SSM框架在Java开发中的应用 如何使用SSM进行web开发

    SSM框架,即Spring、SpringMVC和MyBatis的整合,是Java Web开发中常用的技术栈。它通过分层架构,实现了视图、控制、业务逻辑和数据访问的分离,提高了代码的可维护性和可扩展性
    的头像 发表于 12-16 17:28 2132次阅读