Java并发 - Java中所有的锁

Java 中提供了多种锁机制,用于实现多线程之间的同步和互斥。
在这里插入图片描述

1. 乐观锁&悲观锁

1.1 特点

乐观锁:假定多个事务之间很少发生冲突,操作不加锁。发生错误的时候进行回滚或重试。

悲观锁:假定冲突可能频繁发生,先加锁,阻止其他事务发生,操作后释放锁。

实现机制

乐观锁:实现方式是利用版本号(versioning)或时间戳(Timestamp),在进行更新的时候检查版本号或时间戳是否仍然匹配。

悲观锁:使用传统的锁机制,如synchronize 关键字或 ReetrantLock,执行完成释放锁

1.2 性能
  • 乐观锁:适合读多写少,在高并发写入的情况下,可能出现多次重试,导致性能降低。

  • 悲观锁:时候写多读少,在高并发写入的情况下,可能会有较多的线程竞争,导致性能降低。

1.3 示例

乐观锁:

AtomicInteger version = new AtomicInteger();

public void updateData(Object newData){
    int currentVersion = version.get();
    if (currentVersion == version.get()){
        // 执行更新
        update(newData);
        // 增加版本
        version.incrementAndGet();
    }else {
        throw new OptimisticLockingFailureException("concurrent modification detected");
    }
}

悲观锁:

// 使用ReentrantLock实现悲观锁
ReentrantLock lock = new ReentrantLock();

// 更新操作
public void updateData(Object newData) {
    lock.lock(); // 获取锁
    try {
        // 执行更新
        update(newData);
    } finally {
        lock.unlock(); // 释放锁
    }
}

2. 自旋锁 & 适应性自旋锁

自旋锁(Spin Lock)和适应性自旋锁是两种不同的锁实现,用于在多线程环境中同步访问共享资源。

2.1 特点

自旋锁:

  • 一直自旋,当线程尝试获取锁,如锁已被其他线程占用,一直自旋等待直到获取到锁为止。
  • 不考虑等待时间。

适应性自旋锁:

  • 动态自适应,根据锁的历史信息进行动态调整自旋时间。
  • 考虑等待时间。

优点:

自旋锁:低开销,自旋锁通常比较轻量,适用于锁竞争不激烈的情况。

适应性自旋锁:适应性 ,适应性自旋锁能够在锁的竞争激烈时自适应地减少自旋等待时间,提高效率。

缺点:

自旋锁:高竞争下效率低,在锁竞争激烈的情况下,自旋会导致线程不断自旋,浪费 CPU 资源。

适应性自旋锁:复杂性 ,实现适应性自旋锁的算法较为复杂,可能会引入一些额外的开销。

2.2 性能
  • 自旋锁: 适用于锁竞争不激烈的情况,且期望锁的开销较小。

  • 适应性自旋锁:适用于锁的竞争激烈、存在较多争用的情况,能够根据实际情况调整自旋等待时间。

2.3 示例

自旋锁

import java.util.concurrent.atomic.AtomicReference;

public class SpinLock {
    private AtomicReference<Thread> owner = new AtomicReference<>();

    public void lock() {
        Thread currentThread = Thread.currentThread();
        while (!owner.compareAndSet(null, currentThread)) {
            // 自旋等待
        }
    }

    public void unlock() {
        Thread currentThread = Thread.currentThread();
        owner.compareAndSet(currentThread, null);
    }
}

适应性自旋锁

见 JDK 并发包中的 ReentrantLock

3. 无锁 & 偏向锁 & 轻量级锁 & 重量级锁

这四种锁结构都是针对synchronized来说的。锁的升级是由于竞争的激烈程度导致的,竞争越大,锁结构越重量化。

锁升级实现流程实现
在这里插入图片描述

3.1 特点

无锁: 不使用任何锁机制,通过一些算法或硬件原语实现多线程间的同步,通常在并发度较高的情况下表现优越。

偏向锁: 当一个线程访问同步块并获取锁时,会在对象头上的Mark Word中记录锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行额外的操作。

轻量级锁: 当一个线程尝试获取锁时,如果没有竞争,将在对象头的Mark Word中存储指向锁记录的指针;如果有竞争,会膨胀为重量级锁。

重量级锁: 当一个线程尝试获取锁时,如果存在竞争,将通过操作系统的互斥原语(如操作系统的 Mutex)来实现锁。

优点:

无锁:无竞争开销: 适用于低竞争、高并发的情况,减少锁的开销。

偏向锁:减少竞争: 适用于大多数情况下都是单线程访问同步块的场景,减少无谓的竞争。

轻量级锁:低竞争: 适用于线程间竞争不激烈的情况,减少了传统重量级锁的性能开销。

重量级锁:适应高竞争: 在多线程间存在激烈竞争的情况下,重量级锁能够确保线程安全。

缺点:

无锁:复杂性: 无锁算法相对复杂,实现较为困难。

偏向锁:切换成本: 如果存在多线程竞争,会导致锁膨胀为轻量级锁,引入额外的切换成本。

轻量级锁:自旋等待: 线程可能会进行短暂的自旋等待,一旦竞争激烈,可能会升级为重量级锁。

重量级锁:性能开销: 在竞争激烈的情况下,频繁地切换锁的所有者,可能导致性能下降。

3.2 性能
  • 无锁: 适用于高并发度、低竞争的场景,算法实现较为复杂。
  • 偏向锁: 适用于单线程频繁访问同步块的场景,减少无谓的竞争。
  • 轻量级锁: 适用于线程间竞争不激烈的情况,减少了传统重量级锁的性能开销。
  • 重量级锁: 适用于多线程竞争激烈、需要确保线程安全的场景,但可能会引入较大的性能开销。

4. 公平锁 & 非公平锁

锁可以分为公平锁和非公平锁,它们主要区别于获取锁的顺序。

公平锁 / 非公平锁:

前提:已存在多个线程在排队获取锁,存在阻塞队列。

条件:新来一个线程需要获取资源。

公平锁:在阻塞队列后面排队。

非公平锁:先竞争锁,竞争失败了才到阻塞队列后面排队。

4.1 特点
  • 公平锁

    • 获取锁顺序: 公平锁按照请求锁的顺序获取锁,即先到先得的原则。
    • 等待队列: 线程在等待锁时会进入一个先进先出(FIFO)的等待队列。
    • 实现机制: 公平锁通常使用队列(如 ReentrantLock 中的 FairSync)来维护等待队列。
  • 非公平锁

    • 获取锁顺序: 非公平锁在尝试获取锁时不考虑等待队列中其他线程的顺序,可能会插队成功获取锁。
    • 等待队列: 线程在等待锁时可能会插队直接获取锁,不一定按照请求的顺序。
    • 实现机制: 非公平锁通常使用一种较为简单的机制,避免了公平锁的性能开销。

优点

公平锁:公平性: 所有线程都有机会获取锁,不会出现饥饿现象。

非公平锁:性能: 在高并发的情况下,非公平锁通常具有更好的吞吐量,因为它允许线程插队。

缺点

公平锁:性能开销: 实现公平锁通常需要维护一个等待队列,可能引入额外的性能开销。

非公平锁:可能导致饥饿: 一些线程可能会被其他线程一直插队,导致某些线程一直无法获取锁,可能会出现饥饿现象。

4.2 性能
  • 公平锁: 由于需要维护等待队列,可能在高并发场景下性能相对较低。
  • 非公平锁: 通常具有更好的吞吐量,因为它允许一些线程插队,降低了竞争。
4.3 示例
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
ReentrantLock unfairLock = new ReentrantLock(false); // 非公平锁

5. 可重入锁 & 非可重入锁

锁可以分为可重入锁和非可重入锁,它们主要区别于同一个线程是否可以多次获得同一个锁。

ReentrantLocksynchronized 关键字都是可重入锁的实现。

5.1 特点
  • 可重入锁

    • 可重入性: 同一个线程可以多次获得同一个锁,每次获得锁都会给锁计数器加1,需要相同次数的释放操作才能释放锁。
    • 递归调用: 在递归调用中,线程不会被自己持有的锁所阻塞。
    • 实现机制: Java 中的 ReentrantLocksynchronized 关键字都是可重入锁的实现。
  • 非可重入锁

    • 不可重入性: 同一个线程获得锁后,再次尝试获得时会被阻塞,造成死锁。
    • 不支持递归调用: 在递归调用中,线程会被自己持有的锁所阻塞,无法再次获取。

6. 独享锁(排他锁) & 共享锁

锁可以分为独享锁(排他锁)和共享锁,它们主要用于多线程之间对共享资源的访问控制

6.1 特点
  • 独享锁

    • 获取方式: 独享锁是一种独占式的锁,一次只能被一个线程持有。
    • 互斥性: 当一个线程持有独享锁时,其他线程无法获取该锁,需要等待释放。
    • 操作原子性: 独享锁通常用于保护临界区或关键代码段,确保操作的原子性。
  • 共享锁

    • 获取方式: 共享锁是一种共享式的锁,可以被多个线程同时持有。
    • 允许并发: 多个线程可以同时获取共享锁,允许并发读取共享资源。
    • 读写分离: 共享锁通常用于读多写少的场景,提高读操作的并发性。

优点

独享锁:数据安全: 适用于需要保护临界区或写操作的场景,确保数据的一致性和完整性。

共享锁:高并发: 适用于读操作较多的场景,能够提高系统的并发性。

缺点

独享锁:竞争激烈: 在高并发场景下,独享锁的竞争可能较为激烈,导致性能下降。

共享锁:不适用于写操作: 对于写操作,需要等待其他线程释放共享锁,可能导致写操作的延迟。

6.2 性能
  • 独享锁: 适用于需要保护临界区或关键代码段,确保操作的原子性,写操作较为频繁的场景。
  • 共享锁: 适用于读操作较为频繁的场景,提高系统的并发性,对数据的一致性要求相对较低。
6.3 示例
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

// 共享锁
public int readData() {
    readWriteLock.readLock().lock(); // 获取读锁
    try {
        System.out.println(Thread.currentThread().getName() + " is reading data: " + data);
        return data;
    } finally {
        readWriteLock.readLock().unlock(); // 释放读锁
    }
}

// 独享锁
public void writeData(int newData) {
    readWriteLock.writeLock().lock(); // 获取写锁
    try {
        System.out.println(Thread.currentThread().getName() + " is writing data: " + newData);
        data = newData;
    } finally {
        readWriteLock.writeLock().unlock(); // 释放写锁
    }
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/292314.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux ---- 进程和计划任务

内核功用&#xff1a;进程管理、内存管理、文件系统、网络功能、驱动程序、安全功能等 一、程序和进程的关系 1、程序 保存在硬盘、光盘等介质中的可执行代码和数据静态保存的代码 2、进程 在CPU及内存中运行的程序代码动态执行的代码父、子进程 每个程序可以创建一个或多个…

[Redis实战]分布式锁-redission

五、分布式锁-redission 5.1 分布式锁-redission功能介绍 基于setnx实现的分布式锁存在下面的问题&#xff1a; 重入问题&#xff1a;重入问题就是指获得锁的线程可以再次进入到相同的锁的代码中&#xff0c;可重入锁的意义在于防止死锁。比如HashTable这样的代码中&#xf…

web自动化测试详细流程和步骤

一、什么是web自动化测试 自动化&#xff08;Automation&#xff09;是指机器设备、系统或过程&#xff08;生产、管理过程&#xff09;在没有人或较少人的直接参与下&#xff0c;按照人的要求&#xff0c;经过自动检测、信息处理、分析判断、操纵控制&#xff0c;实现预期的目…

Linux_apachectl 网页优化

1.1 网页压缩与缓存 在使用 Apache 作为 Web 服务器的过程中&#xff0c;只有对 Apache 服务器进行适当的优化配 置&#xff0c;才能让 Apache 发挥出更好的性能。反过来说&#xff0c;如果 Apache 的配置非常糟糕&#xff0c; Apache 可能无法正常为我们服务。因此&#xff0c…

手把手教你在Ubuntu22上安装VideoRetalking

VideoReTalking是一种新系统&#xff0c;可以根据输入音频编辑真实世界的谈话头部视频的面孔&#xff0c;即使具有不同的情感&#xff0c;也能生成高质量和口型同步的输出视频。我们的系统将这个目标分解为三个连续的任务&#xff1a; &#xff08;1&#xff09;具有规范表情的…

【UEFI基础】EDK网络框架(UNDI)

UNDI UNDI代码综述 UNDI全称Universal Network Driver Interface&#xff0c;它虽然属于UEFI网络框架的一部分&#xff0c;但是并没有在EDK开源代码中实现。不过目前主流网卡厂商都会提供UEFI下的网络驱动&#xff0c;并且大部分都实现了UNDI&#xff0c;这样BIOS下就可以通过…

Lazada商品详情API(lazada.item_get)参数详解:如何传递正确的参数

一、引言 随着电子商务的快速发展&#xff0c;获取商品详情成为了电商应用程序中的一项重要功能。Lazada作为东南亚地区知名的电商平台&#xff0c;提供了Lazada商品详情API&#xff08;lazada.item_get&#xff09;以方便开发者获取商品详情。本文将详细介绍如何使用Lazada商…

交换机03_基本配置

一、思科设备的命令行基础 1、进入设备的命令行界面 设备支持命令行 去查看设备上的接口&#xff0c;是否有console口需要有console线 右击此电脑设备管理器需要通过超级终端软件进行连接&#xff0c;如putt、secret CRT、xshell等软件 &#xff08;1&#xff09;思科模拟器…

【LeetCode:34. 在排序数组中查找元素的第一个和最后一个位置 | 二分】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

深入探讨关于Redis的底层

1.1为什么Redis存储比关系型数据库快&#xff1a; 数据存储在内存中&#xff08;比如企业项目中用户表中有一个亿的用户&#xff0c;如果再来注册一个用户&#xff0c;或者登录&#xff0c;必须先判断是否有这个数据&#xff0c;这个时候如果直接查询数据库的话&#xff0c;对服…

指增的超额来自于哪里,2024的乾坤九法,美股的宏观估值双杀

图片截止到&#xff1a;2024/1/4 上证 周四 -0.43% 市场热点分析 1. 2024元旦后国内外市场都出现了不同程度的下跌。技术面国内市场一直走在72日均线之下&#xff0c;而且没有形成底部&#xff0c;熊市还会延续。宏观方面&#xff0c;12月官方PMI持续向下&#xff0c;小企业更多…

SSL/TLS 握手过程详解

SSL握手过程详解 1、SSL/TLS 历史发展2、SSL/TLS握手过程概览2.1、协商交换密码套件和参数2.2、验证一方或双方的身份2.3、创建/交换对称会话密钥 3、TLS 1.2 握手过程详解4、TLS 1.3 握手过程详解5、The TLS 1.2 handshake – Diffie-Hellman Edition 1、SSL/TLS 历史发展 可…

QML 项目中使用 Qt Design Studio 生成的UI界面

作者&#xff1a;billy 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 前言 今天来和大家聊一下 Qt Design Studio 这个软件。这个软件的主要功能是用来快速完成 UI 界面&#xff0c;就和 widget 中的 desig…

(湖科大教书匠)计算机网络微课堂(下)

第四章、网络层 网络层概述 网络层主要任务是实习网络互连&#xff0c;进而实现数据包在各网络之间的传输 因特网使用TCP/IP协议栈 由于TCP/IP协议栈的网络层使用网际协议IP&#xff0c;是整个协议栈的核心协议&#xff0c;因此TCP/IP协议栈的网络层常称为网际层 网络层提供…

1.3 金融数据可视化

跳转到根目录&#xff1a;知行合一&#xff1a;投资篇 已完成&#xff1a; 1.1 编程基础   1.1.1 投资-编程基础-numpy   1.1.2 投资-编程基础-pandas 1.2 金融数据处理 1.3 金融数据可视化 文章目录 1. 金融数据可视化1.1. matplotlib1.1.1. 沪深300走势图1.1.2. 日线均线…

D50|单调栈

739.每日温度 初始思路&#xff1a; 暴力解法但是会超时。 class Solution {public int[] dailyTemperatures(int[] temperatures) {int[] answer new int[temperatures.length];for(int i 0;i<temperatures.length;i){for(int j i;j<temperatures.length;j){if(te…

C# 2中的一些小特性

一、局部类型 在C#当中有这样一个关键字partial 用来声明类&#xff0c;结构&#xff0c;接口分为多个部分来声明。使用场景可以一部分类中写实例方法&#xff0c;一部分写属性&#xff0c;我在实际工作测试中就是将属性与实际方法是分开的。相互之间的成员互相通用。 举个例子…

C# 反射的终点:Type,MethodInfo,PropertyInfo,ParameterInfo,Summry

文章目录 前言反射是什么&#xff1f;常用类型操作SummryPropertyInfoMethodInfo无参函数运行 有参函数运行,获取paramterInfo 总结 前言 我之前写了一篇Attribute特性的介绍&#xff0c;成功拿到了Attribute的属性&#xff0c;但是如果把Attribute玩的溜&#xff0c;那就要彻…

关键字:instanceof关键字

在 Java 中&#xff0c;instanceof关键字用于检查一个对象是否是某个特定类或其子类的实例。它的语法如下&#xff1a; 其中&#xff0c;Object是要检查的对象&#xff0c;Class是要检查的类或接口。 instanceof关键字的返回值是一个布尔值&#xff0c;如果对象Object是类Cla…

基于Spring Boot的美妆分享系统:打造个性化推荐、互动社区与智能决策

基于Spring Boot的美妆分享系统&#xff1a;打造个性化推荐、互动社区与智能决策 1. 项目介绍2. 管理员功能2.1 美妆管理2.2 页面管理2.3 链接管理2.4 评论管理2.5 用户管理2.6 公告管理 3. 用户功能3.1 登录注册3.2 分享商品3.3 问答3.4 我的分享3.5 我的收藏夹 4. 创新点4.1 …