Java锁的分类

news2025/2/25 9:59:28

系列文章目录

第一章 Java线程池技术应用
第二章 CountDownLatch和Semaphone的应用
第三章 Spring Cloud 简介
第四章 Spring Cloud Netflix 之 Eureka
第五章 Spring Cloud Netflix 之 Ribbon
第六章 Spring Cloud 之 OpenFeign
第七章 Spring Cloud 之 GateWay
第八章 Spring Cloud Netflix 之 Hystrix
第九章 代码管理gitlab 使用
第十章 SpringCloud Alibaba 之 Nacos discovery
第十一章 SpringCloud Alibaba 之 Nacos Config
第十二章 Spring Cloud Alibaba 之 Sentinel
第十三章 JWT
第十四章 RabbitMQ应用
第十五章 RabbitMQ 延迟队列
Java锁的分类

在这里插入图片描述


文章目录

  • 系列文章目录
  • 前言
  • 1、公平锁和非公平锁
  • 2、可重入锁
  • 3、独享锁/共享锁
  • 4、互斥锁/读写锁
  • 5、乐观锁/悲观锁
  • 6、分段锁
  • 7、偏向锁/轻量级锁/重量级锁
  • 8、自旋锁


前言

本章节介绍Java中的几种常见的锁:公平锁和非公平锁、可重入锁、独享锁/共享锁、互斥锁/读写锁、乐观锁/悲观锁、分段锁、偏向锁/轻量级锁/重量级锁、自旋锁。

1、公平锁和非公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。
对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁的优点在于吞吐量比公平锁大。
对于synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。

package com.xxxx.reids.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/***
 * @title ReentrantLockFair
 * @desctption 公平锁
 * @author Kelvin
 * @create 2023/5/29 16:10
 **/
public class ReentrantLockFair {

    public static void main(String[] args) {
        //true表示公平锁
        Lock lock = new ReentrantLock(true);
        for (int i = 0; i < 3; i++) {
            new Thread(){
                @Override
                public void run() {
                    for (int j = 0; j < 2; j++) {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName());
                        lock.unlock();
                    }
                }
            }.start();
        }
    }

}

```java
package com.xxxx.reids.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/***
 * @title ReentrantLockFair
 * @desctption 非公平锁
 * @author Kelvin
 * @create 2023/5/29 16:10
 **/
public class ReentrantLockFair {

    public static void main(String[] args) {
        //false表示非公平锁
        Lock lock = new ReentrantLock(false);
        for (int i = 0; i < 3; i++) {
            new Thread(){
                @Override
                public void run() {
                    for (int j = 0; j < 2; j++) {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName());
                        lock.unlock();
                    }
                }
            }.start();
        }
    }

}

2、可重入锁

可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁(就是可以重新进入获得锁)对于Java ReentrantLock而言, 其名字是Reentrant Lock即是重新进入锁。对于synchronized而言,也是一个可重入锁。可重入锁的一个好处是可一定程度避免死锁
(可重入锁,指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。)

synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}

/***
 * @title ReentrantLockTest
 * @desctption 可重入锁测试
 * @author Kelvin
 * @create 2023/5/29 16:29
 **/
public class ReentrantLockTest {

    public static void main(String[] args) {
        Object obj = new Object();
        new Thread(() -> {
            //第一次加锁
            synchronized (obj) {
                System.out.println(Thread.currentThread().getName() + "第一层");
                //第二次加锁,此时obj对象处于锁定状态,但是当前线程仍然可以进入,避免死锁
                synchronized (obj) {
                    int i = 1 / 0;
                    System.out.println(Thread.currentThread().getName() + "第二层");
                }
            }
        }, "t1").start();
        new Thread(() -> {
            //第一次加锁
            synchronized (obj) {
                System.out.println(Thread.currentThread().getName() + "第一层");
                //第二次加锁,此时obj对象处于锁定状态,但是当前线程仍然可以进入,避免死锁
                synchronized (obj) {
                    System.out.println(Thread.currentThread().getName() + "第二层");
                }
            }
        }, "t2").start();
    }

}


练习:用ReentrantLock测试可重入锁

3、独享锁/共享锁

独享锁是指该锁一次只能被一个线程所持有;共享锁是指该锁可被多个线程所持有

对于Java ReentrantLock而言,其是独享锁。但是对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。读锁的共享锁可保证并发读是非常高效的,读写、写读 、写写的过程是互斥的。独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。对于synchronized而言,当然是独享锁。
(读锁使用共享模式;写锁使用独占模式;读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的。当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁)

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/***
 * @title WriteAndReedLockTest
 * @desctption 读写锁
 * @author kelvin
 * @create 2023/5/29 16:58
 **/
public class WriteAndReedLockTest {
    private static ReentrantReadWriteLock reentrantReadWriteLock= new ReentrantReadWriteLock();
    private static ExecutorService executorService = Executors.newFixedThreadPool(3);
    //执行三个线程进行读写操作,并设置一个屏障,线程依次准备就绪后未获取锁之前都在等待,当第三个线程执行 cyclicBarrier.await();后屏障解除,三个线程同时执行。
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    private static Integer i = 100;
    public static void main(String[] args) {
        executorService.execute(
                () ->{
                    read();
                }
        );
        executorService.execute(
                () ->{
                    write();
                }
        );
        executorService.execute(
                () ->{
                    read();
                }
        );
    }

    private static void read(){
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        //获取读锁
        reentrantReadWriteLock.readLock().lock();
        System.out.println("Read," + Thread.currentThread().getName() + ",i = " + i);
        reentrantReadWriteLock.readLock().unlock();
    }

    private static void write(){
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        //获取写锁
        reentrantReadWriteLock.writeLock().lock();
        i ++;
        System.out.println("Write," + Thread.currentThread().getName() + ",i = " + i);
        reentrantReadWriteLock.writeLock().unlock();
    }
}

4、互斥锁/读写锁

独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。互斥锁在Java中的具体实现就是ReentrantLock;读写锁在Java中的具体实现就是Read/WriteLock。

5、乐观锁/悲观锁

乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。

  • 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如 Java 里面的同步原语 synchronized 关键字的实现也是悲观锁
  • 乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于 write_condition 机制,其实都是提供的乐观锁。在 Java中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的
  • 在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS(Compare and Swap 比较并交换)实现的。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/***
 * @title AtomicExample
 * @desctption 乐观锁/悲观锁
 * @author Kelvin
 * @create 2023/5/29 17:08
 **/
public class AtomicExample {
    private static Integer m = 1;
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 1000; i++) {
            executorService.submit(
                    () -> {
                        m ++;
                    }
            );
        }
        TimeUnit.SECONDS.sleep(2);
        System.out.println(m);
    }
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/***
 * @title AtomicExample
 * @desctption 乐观锁/悲观锁
 * @author Kelvin
 * @create 2023/5/29 17:08
 **/
public class AtomicExample {
    private static AtomicInteger m = new AtomicInteger(1);
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        for (int i = 0; i < 1000; i++) {
            executorService.submit(
                    () -> {
                        m.incrementAndGet();
                    }
            );
        }
        TimeUnit.SECONDS.sleep(2);
        System.out.println(m.get());
    }
}

6、分段锁

分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。当需要put元素的时候,并不是对整个HashMap进行加锁,而是先通过hashcode来知道他要放在那一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。但是,在统计size的时候,可就是获取HashMap全局信息的时候,就需要获取所有的分段锁才能统计。

分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

7、偏向锁/轻量级锁/重量级锁

这三种锁是指锁的状态,并且是针对synchronized。在Java 5通过引入锁升级的机制来实现高效synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的。

  • 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
  • 轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
  • 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

8、自旋锁

在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于Web Tours

Web Tours 文章目录 简介下载解压安装并启动地址注册登录特殊情况 简介 Web Tours是惠普loadrunner自带的一个飞机订票系统网站&#xff0c;默认支持SQL Server数据库、Access、Mysql等多种数据库&#xff0c;基于ie、Chrome、Firefox等浏览器&#xff0c;Web Tours网站主要提…

Jenkins-执行脚本案例-初步认识JenKins的使用

环境搭建 docker pull jenkins/jenkins:2.440 docker run -d -p 10240:8080 -p 10241:50000 -v /env/liyong/data/docker/jenkins_mount:/var/jenkins_home -v /etc/localtime:/etc/localtime --name jenkins jenkins/jenkins:2.440 #在挂载的目录下去修改仓库地址 vim hudson…

mysql复制表的几种常用方法

遇到需要拷贝一个表及其数据的情况,总结了一下几种方法 1.使用 show create table 旧表 将结果拷贝出来,将旧表名换成新表名即可. 注意:该方法仅适用于拷贝表结构,不需要同步数据的情况 show create table 旧表名2.create table 新表 like 旧表 该语句将完全拷贝旧表结构, …

15个为你的品牌增加曝光的维基百科推广方法-华媒舍

维基百科是全球最大的免费在线百科全书&#xff0c;拥有庞大的用户群体和高质量的内容。在如今竞争激烈的市场中&#xff0c;利用维基百科推广品牌和增加曝光度已成为许多企业的重要策略。本文将介绍15种方法&#xff0c;帮助你有效地利用维基百科推广品牌&#xff0c;提升曝光…

代码随想录算法训练营第四天|24. 两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II,总结

系列文章目录 代码随想录算法训练营第一天|数组理论基础&#xff0c;704. 二分查找&#xff0c;27. 移除元素 代码随想录算法训练营第二天|977.有序数组的平方 &#xff0c;209.长度最小的子数组 &#xff0c;59.螺旋矩阵II 代码随想录算法训练营第三天|链表理论基础&#xff…

【IDEA】瑞_IDEA模版注释设置_IDEA自动生成注释模版(详细图文步骤)

文章目录 1 概要2 类的自定义模版注释3 自定义模版注释3.1 方法的自定义模版注释3.2 属性的自定义模版注释 &#x1f64a; 前言&#xff1a;在Java开发中&#xff0c;注释具有不可或缺的重要性&#xff0c;注释负责解释代码&#xff0c;能帮助开发人员深入理解代码的逻辑和功能…

强化学习应用(三):基于Q-learning算法的无人车配送路径规划(提供Python代码)

一、Q-learning算法介绍 Q-learning是一种强化学习算法&#xff0c;用于解决基于环境的决策问题。它通过学习一个Q-table来指导智能体在不同状态下采取最优动作。下面是Q-learning算法的基本步骤&#xff1a; 1. 定义环境&#xff1a;确定问题的状态和动作空间&#xff0c;并…

宝塔面板安装MySQL8数据库

第一步&#xff1a;搜索mysql 第二步: 点击安装 我这里选择安装8版本 第三步&#xff1a;给宝塔配置mysql防火墙 第四步&#xff1a;修改数据库密码 第五步&#xff1a;想要使用navicat连接 需要修改root的权限 &#xff08;1&#xff09;使用secureCRT先登录mysql (2) 输入u…

Sentinel限流、熔断

1、限流 单个服务节点限流 sentinel 提供了两种不同的隔离机制&#xff1a;信号量隔离和线程池隔离&#xff0c;它们的主要区别如下&#xff1a; 信号量隔离&#xff08;Semaphore Isolation&#xff09;&#xff1a; 原理&#xff1a;信号量隔离基于计数器&#xff08;或称令…

电池均衡管理

一、前言 在电芯批量生产过程中&#xff0c;由于原料及生产工艺的波动&#xff0c;电芯的容量、内阻、电压及自放电率均会有一定的偏差&#xff0c;同时在电芯使用过程中随着充放电循环次数增加及存储时间、温度等影响&#xff0c;电芯容量衰减也会出现不一致&#xff0c;导致在…

有趣可操控的无人机足球大赛首次亮相,CES2024上的那些黑科技(二)

今年的CES2024&#xff08;国际消费电子展&#xff09;可谓是黑科技的盛宴&#xff0c;前天我们分享了5个有意思的产品&#xff0c;今天继续带来5个&#xff0c;一同探寻那些令人兴奋的黑科技&#xff0c;让我们的未来更加有趣和智能。 1400元巴掌大小AI硬件&#xff0c;首日卖…

STM32之OLED显示

一、模块介绍 1、常见的显示设备 LED、数码管、点阵、LCD屏(1602/12864)、OLED屏(消费电子) 2、OLED屏的概述 OLED&#xff0c;即有机发光二极管&#xff08;Organic Light-Emitting Diode&#xff09;&#xff0c;又称为有机电激光显示&#xff08;Organic Electroluminesenc…

4、C语言:指针与数组

数组与指针 指针与地址指针与函数参数指针与数组地址算数运算字符指针与函数指针数组以及指向指针的指针多维数组命令行参数指向函数的指针复杂声明 指针是一种保存变量地址的变量。C语言中&#xff0c;指针的使用非常广泛&#xff0c;原因之一是&#xff0c;指针常常是表达某个…

IaC基础设施即代码:使用Terraform 连接 alicloud阿里云

目录 一、实验 1.环境 2.alicloud阿里云创建用户 3.Linux使用Terraform 连接 alicloud 4.Windows使用Terraform 连接 alicloud 二、问题 1.Windows如何申明RAM 相关变量 2.Linux如何申明RAM 相关变量 3. Linux terraform 初始化失败 4.Linux terraform 计划与预览失败…

[ACM算法学习] 诱导排序与 SA-IS算法

学习自诱导排序与SA-IS算法 - riteme.site 为了简化一些操作&#xff0c;定义 # 是字典序最小的字符&#xff0c;其字典序小于字母集里任意字符&#xff0c;并且将其默认作为每个字符串的最后一个字符&#xff0c;即作 S[|S|] SA-IS 算法 SA-IS 算法是基于诱导排序这种思想。…

React入门 - 06(TodoList 列表数据的新增和删除)

本章内容 目录 一、实践一下 React 的列表渲染二、TodoList 新增功能三、列表循环的 key四、删除 上一节内容我们完成了输入框中可以自由输入内容&#xff0c;这一节我们继续 TodoList功能的完善&#xff1a;列表数据的新增和删除。 在开始之前&#xff0c;我们先介绍一下 Re…

java实现教务管理系统

git地址&#xff1a;https://gitee.com/myshort-term/school-management 1.系统简介 开发教务管理系统程序&#xff0c;设计了ems&#xff08;EMSApp&#xff09;、dao&#xff08;AssignmentDAO、CourseDAO、DeptDAO、ScoreDAO、UserDAO、EmailDAO&#xff09;、domain&#…

优雅草蜻蜓API大数据服务中心v1.0.4更新-加入蓝奏云直链解析·每日Bing·字数统计·今日油价·历史上的今天等接口

2024年1月13日优雅草蜻蜓API大数据服务中心v1.0.4更新-加入蓝奏云直链解析每日Bing字数统计今日油价历史上的今天等接口 优雅草api服务-大数据中心自12月29日推出以来截止2024年1月13日累计被调用次数为413次&#xff0c;共收录23个接口&#xff0c;截止前一日2024年1月12日当…

一元二次方程虚数解

对一元二次方程axbxc0 (a≠0)&#xff1b;若判别式△b-4ac<0,则方程无实根,虚数解为&#xff1a;x(-b i√(4ac-b))/(2a)。 只含有一个未知数&#xff08;一元&#xff09;&#xff0c;并且未知数项的最高次数是2&#xff08;二次&#xff09;的整式方程叫做一元二次方程[1] …