您现在的位置是:首页 > 文章 > Java 中各种锁的介绍 网站文章

Java 中各种锁的介绍

孙玉超 2021-04-06 14:47:31 0 评论 747 浏览 0 收藏 0


锁的概念有很多,并不局限于语言。由于本人着重于 Java 语言,今天以 Java 语言为例,谈谈 Java 中各种锁。梳理之后 Java 中大体上有以下几种锁:


● 悲观锁、乐观锁

● 互斥锁、共享锁

● 公平锁、非公平锁

● 可重入锁、不可重入锁

● 无锁、偏向锁、轻量级锁、重量级锁


了解这些之前要知道,以上的锁只是一种广义上的概念,或者说它们并不是锁的具体状态,后面会介绍它们在 Java 中的具体实现。


悲观锁、乐观锁


悲观锁:它认为每次修改数据都会有其他线程来并发修改,所以直接加锁。Java 中的 synchronized 关键字和 Lock 接口的实现类就是典型的悲观锁。

乐观锁:它其实并不是锁,而是一种解决并发修改数据的一种方案。它认为每次修改数据都不会有其他线程并发修改,因此不加锁。只是最后提交修改的时候会判断这个数据是否被其他线程改动过。Java 中的 CAS 就是一种乐观锁的实现。数据库中也有乐观锁实现,通过版本号判断数据有没有被其他线程修改过。


互斥锁、共享锁


共享锁:多个线程可以共用同一把锁。线程 A 对数据加上共享锁,锁未释放期间,其他线程不能再对数据加互斥锁。共享锁只能读取数据不能修改。Java 中 ReentrantReadWriteLock 的 ReadLock 就是共享锁的具体实现。

ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
            readLock.lock();
            try {
                //处理业务
            }finally {
                readLock.unlock();
            }

互斥锁:与共享锁相反,互斥锁同一时间只能有一个线程持有锁。Java 中 synchronized 关键字、ReentrantReadWriteLock 中的 WriteLock 就是典型的互斥锁实现。

ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
            writeLock.lock();
            try {
                //处理业务
            }finally {
                writeLock.unlock();   
            }

            synchronized (this){
                //处理业务
            }


公平锁、非公平锁


公平锁:多个线程竞争同一把锁,按照先后顺序在一个队列中排队。ReentrantLock 是这两种锁的具体实现,构造方法传入 true 来指定公平锁。

非公平锁:多个线程竞争同一把锁,看 CPU 调度到谁,谁就抢占到锁。ReentrantLock 构造方法传入 false 来指定非公平锁。另外,synchronized 关键字也是非公平锁。


可重入锁、不可重入锁


可重入锁:可重入锁以线程为维度,同一个线程中一段同步代码 A ,锁未释放期间 ,内部另一块同步代码 B 如果又使用的是同一把锁,那么 B 不需要等待 A 释放锁。或者两段同步代码块位于嵌套方法中,比如方法 A 调用 方法 B ,方法 A 和 方法 B 使用同一把锁,那么 B 也不需要等待 A 释放锁。Java 中 synchronized 和 ReentrantLock 就是典型的可重入锁。

synchronized (LockTest.class){
            System.out.println("A");
            synchronized (LockTest.class){
                System.out.println("B");
            }
        }

不可重入锁:如果不可重入,那么上述可重入的例子就会死锁,我没搞明白不可重入锁的意义何在......


无锁、偏向锁、轻量级锁、重量级锁


这几个概念是针对 Java 中的 synchronized 关键字的锁升级的不同过程。对象锁状态记录在对象头里面,从网上盗图,没找到 64 位的图,64 位也类似......

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v0XHNIKH-1571208138226)(图片上传失败)]

偏向锁:一段代码被一直同一个线程访问,该锁就会记录这个线程的 ID ,偏向该线程,提高获取锁的性能。

轻量级锁:当锁为偏向锁时,如果有其他线程来竞争,并且原线程处于存活状态(如果持有偏向锁的线程不在了,锁会偏向来竞争的那个线程而不会升级),偏向锁就会升级为轻量级锁。轻量级锁状态下,竞争不到锁的线程会自旋。

重量级锁:当轻量级锁状态下,竞争锁的线程自旋到一定次数之后会升级为重量级锁。重量级锁状态下,竞争锁的线程会阻塞。


为什么会有锁升级的改动?为了提高性能呗,JDK 1.6 之前是没有这个锁升级概念的,1.6 之后引入锁升级不同状态来提高获取锁释放锁的性能,几种锁分别有适合的场景,以下表格来自 《大话Java性能优化》

优点缺点适用场景
偏向锁加锁和解锁不需要额外的消耗,和执行非同步方法相比仅仅存在纳秒级差距如果线程间存在锁竞争,会带来额外的锁撤销消耗使用用户只有一个线程访问同步块的场景
轻量级锁竞争的线程不会阻塞,提高了程序的响应速度如果始终得不到竞争的线程,自旋会持续消耗CPU追求响应时间,同步块执行速度非常快
重量级锁线程竞争不会自旋,不消耗 CPU 线程阻塞,响应时间较慢追求吞吐量,同步块执行时间较长


转载请注明出处:转载请注明出处

上一篇 : 我的生活 下一篇 : Redis 实现分布式锁 && Redisson

留言评论

所有回复

暮色妖娆丶

96年草根站长,2019年7月接触互联网踏入Java开发岗位,喜欢前后端技术。对技术有强烈的渴望,2019年11月正式上线自己的个人博客