首页 > 编程语言 >java中的各种锁

java中的各种锁

时间:2023-06-22 22:11:28浏览次数:50  
标签:各种 java synchronized Lock 获取 加锁 线程 Java

java中的各种锁

一、概述

  1. 乐观锁 CAS
  2. 悲观锁 synchronized
  3. 自旋锁 CAS
  4. 可重入锁 synchronized、Reentrantlock、Lock
  5. 读写锁 ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet
  6. 公平锁 Reentrantlock(true)
  7. 非公平锁 synchronized、reentrantlock(false)
  8. 共享锁 ReentrantReadWriteLock中读锁
  9. 独占锁 synchronized、ReentrantReadWriteLock中写锁
  10. 重量级锁 synchronized
  11. 轻量级锁 锁优化技术
  12. 偏向锁 锁优化技术
  13. 分段锁 concurrentHashMap(jdk7)
  14. 死锁 相互请求对方的资源
  15. 锁粗化 锁优化技术
  16. 锁消除 锁优化技术

二、乐观锁

乐观锁是一种乐观思想,假定当前环境是读多写少,遇到并发写的概率比较低,读数据时认为别的线程不会正在进行修改(所以没有上锁)。写数据时,判断当前 与期望值是否相同,如果相同则进行更新(更新期间加锁,保证是原子性的)。

CAS:比较并替换,比较当前值(主内存中的值),与预期值(当前线程中的值,主内存中值的一份拷贝)是否一样,一样则更新,否则继续进行CAS操作。

三、悲观锁

悲观锁锁是一种悲观思想,即认为写多读少,遇到并发写的可能性高,每次去拿数据的时候都认为其他线程会修改,所以每次读写数据都会认为其他线程会修改,所以每次读写数据时都会上锁。其他线程想要读写这个数据时,会被这个线程block,直到这个线程释放锁然后其他线程获取到锁。

synchronized修饰的方法和方法块、ReentrantLock。

四、自旋锁

自旋锁是一种技术: 为了让线程等待,我们只须让线程执行一个忙循环(自旋)。现在绝大多数的个人电脑和服务器都是多路(核)处理器系统,如果物理机器有一个以上的处理器或者处理器核心,能让两个或以上的线程同时并行执行,就可以让后面
请求锁的那个线程“稍等一会”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。

优点: 避免了线程切换的开销。挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给Java虚拟机的并发性能带来了很大的压力。

缺点: 占用处理器的时间,如果占用的时间很长,会白白消耗处理器资源,而不会做任何有价值的工作,带来性能的浪费。因此自旋等待的时间必须有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程。

默认值:10次,可以使用参数-XX:PreBlockSpin来自行更改。

自适应自旋: 自适应意味着自旋的时间不再是固定的,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的。有了自适应自旋,随着程序运行时间的增长及性能监控信息的不断完善,虚拟机对程序锁的状态预测就会越来越精准。

Java中的自旋锁: CAS操作中的比较操作失败后的自旋等待。

五、可重入锁

可重入锁是一种技术: 任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞。

的原理: 通过组合自定义同步器来实现锁的获取与释放。

  • 再次获取锁:识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。获取锁后,进行计数自增;
  • 释放锁:释放锁时,进行计数自减。

可重入锁: ReentrantLock、synchronized修饰的方法或代码段。
可重入锁的作用: 避免死锁

六、读写锁

读写锁是一种技术,通过ReentrantReadWriteLock类来实现。为了提高性能, Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。 读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由 jvm 自己控制的。

读锁: 允许多个线程获取读锁,同时访问同一个资源。
写锁: 只允许一个线程获取写锁,不允许同时访问同一个资源。

Java中的读写锁:ReentrantReadWriteLock

/*** 创建一个读写锁* 它是一个读写融为一体的锁,在使用的时候,需要转换*/
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); 
// 获取读锁
rwLock.readLock().lock();
// 释放读锁
rwLock.readLock().unlock(); 
// 创建一个写锁
rwLock.writeLock().lock();
// 写锁 释放
rwLock.writeLock().unlock(); 
12345678910

七、公平锁

公平锁是一种思想,多个线程按照申请锁的顺序来获取锁。在并发环境中,每个线程会先查看此锁维护的等待队列,如果当前等待队列为空,则占有锁,如果等待队列不为空,则加入到等待队列的末尾,按照FIFO的原则从队列中拿到线程,然后占有锁。

八、非公平锁

非公平锁是一种思想,线程尝试获取锁,如果获取不到,则再采用公平锁的方式。多个线程获取锁的顺序,不是按照先到先得的顺序,有可能后申请锁的线程比先申请的线程优先获取锁。

优点: 非公平锁的性能高于公平锁。
缺点: 有可能造成线程饥饿(某个线程很长一段时间获取不到锁)。

Java中的非公平锁:synchronized是非公平锁,ReentrantLock通过构造函数指定该锁是公平的还是非公平的,默认是非公平的

九、共享锁

共享锁*是一种思想: 可以有多个线程获取读锁,以共享的方式持有锁。和乐观锁、读写锁同义。

Java中用到的共享锁:ReentrantReadWriteLock中读锁。

十、独占锁

独占锁是一种思想, 只能有一个线程获取锁,以独占的方式持有锁。和悲观锁、互斥锁同义。

Java中用到的独占锁: synchronized,ReentrantLock

11、重量级锁

重量级锁是一种称谓:synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的,监视器锁本身依赖底层的操作系统的 Mutex Lock来实现。操作系统实现线程的切换需要从用户态切换到核心态,成本非常高。这种依赖于操作系统 Mutex Lock来实现的锁称为重量级锁。为了优化synchonized,引入了轻量级锁,偏向锁。

Java中的重量级锁: synchronized

12、轻量级锁 、偏向锁

轻量级锁是JDK6时加入的一种锁优化机制: 轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量。轻量级是相对于使用操作系统互斥量来实现的重量级锁而言的。轻量级锁在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。如果出现两条以上的线程争用同一个锁的情况,那轻量级锁将不会有效,必须膨胀为重量级锁。

  • 优点: 如果没有竞争,通过CAS操作成功避免了使用互斥量的开销。
  • 缺点: 如果存在竞争,除了互斥量本身的开销外,还额外产生了CAS操作的开销,因此在有竞争的情况下,轻量级锁比传统的重量级锁更慢。

偏向锁是JDK6时加入的一种锁优化机制: 在无竞争的情况下把整个同步都消除掉,连CAS操作都不去做了。偏是指偏心,它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁一直没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作(例如加锁、解锁及对Mark Word的更新操作等)。

  • 优点: 把整个同步都消除掉,连CAS操作都不去做了,优于轻量级锁。
  • 缺点: 如果程序中大多数的锁都总是被多个不同的线程访问,那偏向锁就是多余的。

13、分段锁

分段锁是一种机制: 最好的例子来说明分段锁是ConcurrentHashMap(jdk7)。

原理:它内部细分了若干个小的 HashMap,称之为段(Segment)。默认情况下一个 ConcurrentHashMap 被进一步细分为 16 个段,既就是锁的并发度。如果需要在 ConcurrentHashMap 添加一项key-value,并不是将整个 HashMap 加锁,而是

  • 首先根据 hashcode 得到该key-value应该存放在哪个段中,
  • 然后对该段加锁,并完成 put 操作。
  • 在多线程环境中,如果多个线程同时进行put操作,只要被加入的key-value不存放在同一个段中,则线程间可以做到真正的并行。
  • ConcurrentHashMap 是一个 Segment 数组, Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全

14、死锁

死锁是一种现象:如线程A持有资源x,线程B持有资源y,线程A等待线程B释放资源y,线程B等待线程A释放资源x,两个线程都不释放自己持有的资源,则两个线程都获取不到对方的资源,就会造成死锁。

Java中的死锁不能自行打破,所以线程死锁后,线程不能进行响应。所以一定要注意程序的并发场景,避免造成死锁。

15、锁粗化

锁粗化是一种优化技术: 如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作都是出现在循环体体之中,就算真的没有线程竞争,频繁地进行互斥同步操作将会导致不必要的性能损耗,所以就采取了一种方案:把加锁的范围扩展(粗
化)到整个操作序列的外部,这样加锁解锁的频率就会大大降低,从而减少了性能损耗。

16、锁消除

锁消除是一种优化技术: 就是把锁干掉。当Java虚拟机运行时发现有些共享数据不会被线程竞争时就可以进行锁消除。

那如何判断共享数据不会被线程竞争?

利用逃逸分析技术:分析对象的作用域,如果对象在A方法中定义后,被作为参数传递到B方法中,则称为方法逃逸;如果被其他线程访问,则称为线程逃逸。在堆上的某个数据不会逃逸出去被其他线程访问到,就可以把它当作栈上数据对待,认为它是线程私有的,同步加锁就不需要了。

17、Lock和synchronized的区别

Lock: 是Java中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁。

  • .Lock 是一个接口,而 synchronized 是 Java 中的关键字,
  • ReentrantLock 是 API 级别的, synchronized 是 JVM 级别的
  • Lock需要手动获取锁和释放锁。
  • ReentrantLock 通过 Condition 可以绑定多个条件

synchronized 是内置的语言实现。

  • synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致 死锁现象发生;而 Lock 在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁。
  • Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用synchronized 时,等待的线程会一直等待下去,不能够响应中断。
  • 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
  • Lock 可以通过实现读写锁提高多个线程进行读操作的效率。
  • ReentrantLock 可以实现公平锁、非公平锁,默认非公平锁,synchronized 是非公平锁,且不可更改。

使用建议:

  • 足够清晰简单,只需要基础的同步功能时,用synchronized。
  • Lock应该确保在finally块中释放锁。如果使用synchronized,JVM确保即使出现异常,锁也能被自动释放。
  • 使用Lock时,Java虚拟机很难得知哪些锁对象是由特定线程锁持有的。

除非需要使用 ReentrantLock 的高级功能,比如通过 Condition 绑定多个条件判断。否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。

标签:各种,java,synchronized,Lock,获取,加锁,线程,Java
From: https://www.cnblogs.com/javaxubo/p/17498448.html

相关文章

  • Java Web自定义MVC框架详解
    最近给学生讲JavaWeb,希望他们能够在学完这部分内容后自己实现一个MVC框架。但是突然发现百度上能搜索到的靠谱的资料并不是很多,有些只是原理没有代码实现,有些有代码实现但是对于初学者来说理解起来还是比较困难,于是决定把自己讲自定义MVC框架的内容放在这里分享给大家,不仅仅是代码......
  • 字符编码和Java中的乱码问题
    ASCII码  在计算机内部,所有的信息最终都表示为一堆二进制形式的数据。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,称为一个字节(byte),从0000000到11111111。上世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系做了统一规定,称之为ASCII......
  • Java Web项目中使用Freemarker生成Word文档
    Web项目中生成Word文档的操作屡见不鲜,基于Java的解决方案也是很多的,包括使用Jacob、ApachePOI、Java2Word、iText等各种方式,其实在从Office2003开始,就可以将Office文档转换成XML文件,这样只要将需要填入的内容放上${}占位符,就可以使用像Freemarker这样的模板引擎将出现占位符的地......
  • [连载]Java程序设计(05)---任务驱动方式:简单的加密/解密系统
    任务:还是上一家公司,现在该公司在全国各地都设立了自己的分公司以拓展其核心业务,那么就需要利用互联网在全国各地的公司之间传递信息(我们假定这些信息就是文字信息),这些信息可能涉及一些商业机密,为此公司需要一套简单的加密和解密系统来避免直接在互联网上传递明文信息。目前拟定了两......
  • 历史最全Java资源大全中文版整理分享
       很多程序员应该记得GitHub上有一个Awesome-XXX系列的资源整理。本资源对Java相关的资源列表进行翻译和整理,内容包括:构建工具、数据库、框架、模板、安全、代码分析、日志、第三方库、书籍、Java站点等。分享给需要的朋友。目录内容截图......
  • 第三次java博客
    在Java编程领域中,掌握如何读取和处理用户输入数据是非常基础和重要的一步。我们需要从用户那里获取输入数据,并根据输入数据进行相应的操作或计算。在这篇博客中,我们将会探讨如何在Java程序中读取和处理用户输入数据,以及如何利用这些数据来完成一些常见的任务,比如计算平均分和总成......
  • java Condition类的详细介绍
    一、condition介绍及demoCondition是在java1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,阻塞队列......
  • JAVA:Collections类的shuffle()方法
    Java.util.Collections类下有一个静态的shuffle()方法,如下:1)staticvoidshuffle(List<?>list)使用默认随机源对列表进行置换,所有置换发生的可能性都是大致相等的。2)staticvoidshuffle(List<?>list,Randomrand)使用指定的随机源对指定列表进行置换,所有置换发生的可能性......
  • org.springframework.boot.builder.SpringApplicationBuilder.init([LjavalangObject;
    一SpringBoot2.0.4集成SpringCloud异常:org.springframework.boot.builder.SpringApplicationBuilder.([Ljava/lang/Object;)V二、异常处理参考:缘起初学springcloud的朋友可能不知道,其实SpringBoot与SpringCloud需要版本对应,否则可能会造成很多意料之外的错误,比如eureka注册了......
  • Java 基本数据类型 - 四类八种
    感谢:https://zhuanlan.zhihu.com/p/25439066八种基本数据类型分成四个大类1、整型byte、short、int、long2、浮点型float、double3、字符型char4、布尔型boolean数据类型详细介绍整型(byte、short、int、long)虽然byte、short、int、long数据类型都......