当前位置: 首页 > article >正文

ReentrantLock(可重入锁) Semaphore(信号量) CountDownLatch

目录

  • ReentrantLock(可重入锁) &Semaphore(信号量)&CountDownLatch
    • ReentrantLock(可重入锁)
    • 既然有了synchronized,为啥还要有ReentrantLock?
    • Semaphore(信号量)
    • 如何确保线程安全呢?
    • CountDownLatch

ReentrantLock(可重入锁) &Semaphore(信号量)&CountDownLatch

ReentrantLock(可重入锁)

上古时期的Java中,synchronized不够强壮,功能也不够强大,也没有各种优化

ReentrantLock就是用来实现可重入锁的选择(历史遗留)

后来synchronized变得厉害了,ReentrantLock用的少了,但是仍有一席之地

synchronized也是可重入锁

传统的锁的风格,这个对象提供了两个方法: lock unlock 这个写法,就容易引起,加了锁之后,忘记解锁了

在unlock之前,触发了return或者异常,就可能引起unlock执行不到了

正确使用ReentrantLock就需要把unlock操作放到finally中

既然有了synchronized,为啥还要有ReentrantLock?

1.ReentrantLock提供了tryLock操作

lock直接进行加锁,如果加锁不成,就要阻塞

tryLock尝试进行加锁,如果加锁不成,不阻塞,直接返回false

此处通过tryLock提供了更多的“可操作空间”

2.ReentrantLock提供了公平锁的实现

ReentrantLock提供了公平锁的实现,通过队列记录加锁线程的先后顺序

synchronized是非公平锁

遵循先来后到,就是公平

3.搭配的等待通知机制不同

对于synchronized,搭配wait/notify

对于ReentrantLock,搭配 Condition类,功能比 wait/notify略强一点

实际上绝大部分开发中,使用synchronized就够用了

Semaphore(信号量)

信号量是由迪杰斯特拉提出来的

信号量也是操作系统内部给咱们提供的一个机制

操作系统对应的API被JVM封装了下,就可以通过Java代码来调用这里的相关操作了

所谓的锁,本质上也是一种特殊的信号量,

锁,可以认为就是计数值为1的信号量

释放状态,就是1

加锁状态,就是0

对于这种非0即1的信号量,称为“二元信号量”

信号量是更广义的锁

public class ThreadDemo37 {
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore=new Semaphore(1);
        semaphore.acquire();
        System.out.println("申请1");
        semaphore.acquire();
        System.out.println("申请2");
        semaphore.acquire();
        System.out.println("申请3");
    }
}

在这里插入图片描述

import java.util.concurrent.Semaphore;


public class ThreadDemo38 {
    public static  int count=0;
    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore=new Semaphore(1);
        Thread t1=new Thread(()->{
           for(int i=0;i<500;i++){
               try {
                   semaphore.acquire();
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               count++;
               semaphore.release();

           }
        });
        Thread t2=new Thread(()->{
            for(int i=0;i<500;i++){
                try {
                    semaphore.acquire();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                count++;
                semaphore.release();

            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(count);
    }
}

在这里插入图片描述

如何确保线程安全呢?

1.synchronized

2.ReentrantLock

3.CAS(原子类)

4.Semaphore

semaphore也可以用来实现生产者消费者模型

定义两个信号量

一个用来表示,队列中有多少个可以被消费的元素,sem1

另一个用来表示队列中有多少个可以放置新元素的空间 ,sem2

生产一个元素,sem1.V() , sem2.P()

消费一个元素,sem1.P() , sem2.V()

CountDownLatch

针对特定场景解决问题的小工具!

比如,多线程执行一个任务,把大的任务拆分成几个部分,由每个线程分别执行

下载一个文件,可能很大,但是可以拆成多个部分,每个线程负责下载一部分,下载完成之和,最终把下载的结果拼在一起。

像多线程下载这样的场景,最终执行完成之后,要把所有内容拼在一起

这个拼必然要等到所有线程执行完成

使用CountDownLatch就可以很方便的感知到这个事情!(比你调用很多次join要更简单方便一些)

如果使用join方式,就只能使用每个线程执行一个任务

借助CountDownLatch就可以让一个线程能执行很多个任务

import java.util.Random;
import java.util.concurrent.CountDownLatch;


public class ThreadDemo39 {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch=new CountDownLatch(10);
        for(int i=0;i<10;i++){
            int id=i;
            Thread t=new Thread(()->{
                System.out.println("任务:"+id+",开始下载");
                Random random = new Random();
                // [0, 5)
                int time = (random.nextInt(5) + 1) * 1000;
                try {
                    Thread.sleep(time);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("任务:"+id+",结束下载");
                latch.countDown();

            }) ;
            t.start();
        }
        latch.await();
        System.out.println("所有下载完毕");
    }
}

在这里插入图片描述


http://www.kler.cn/a/411348.html

相关文章:

  • SycoTec 4060 ER-S德国高精密主轴电机如何支持模具的自动化加工?
  • springboot项目使用maven打包,第三方jar问题
  • FileLink内外网文件共享系统与FTP对比:高效、安全的文件传输新选择
  • 离散化 C++
  • 正则表达式灾难:重新认识“KISS原则”的意义
  • 【MySQL】数据库核心技术与应用指南
  • Windows下安装FreeSurfer教程
  • Linux进程信号保存/操作系统运行原理
  • 【第三讲】Spring Boot 3.4.0 新特性详解:增强的配置属性支持
  • 无人机:智能航点规划技术!
  • jQuery-Json-AJAX-跨域
  • 【前端】javaScript
  • Excel与PPT:职场两大软件的应用比拼
  • 调用 AWS Lambda 时如何传送字节数组
  • 关于IDE的相关知识之三【插件安装、配置及推荐的意义】
  • Python语法基础(一)
  • 【C++】C++的nullptr和NULL
  • vue的理解
  • 鸿蒙学习自由流转与分布式运行环境-价值与架构定义(1)
  • 【C++】顺序容器(二):顺序容器操作
  • C++11新特性探索:Lambda表达式与函数包装器的实用指南
  • 极狐GitLab 17.6 正式发布几十项与 DevSecOps 相关的功能【四】
  • STM32中I2C总线中,允许从机控制SCL总线吗?
  • uname -m(machine) 命令用于显示当前系统的机器硬件架构(Unix Name)
  • 什么是 C++ 中的多继承?它有哪些优缺点?什么是虚继承?为什么要使用虚继承?
  • OSPTrack:一个包含多个生态系统中软件包执行时生成的静态和动态特征的标记数据集,用于识别开源软件中的恶意行为。