返回顶部
首页 > 资讯 > 后端开发 > JAVA >【JavaEE】JUC(Java.util.concurrent)常见类
  • 922
分享到

【JavaEE】JUC(Java.util.concurrent)常见类

javajava-ee信号量ReentrantLock多线程CountDownLatch 2023-10-10 15:10:33 922人浏览 安东尼
摘要

文章目录 前言ReentrantLock原子类线程池信号量CountDownLatch相关面试题 前言 经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安

在这里插入图片描述

文章目录

前言

经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安全问题,java.util.concurrent 是我们多线程编程的一个常用包,那么今天我将为大家分享 java.util.concurrent 包下的其他几种常见的类。

ReentrantLock

ReentrantLock 是可重入互斥,跟 synchronized 定位是类似的,都是用来保证线程安全的,但是 ReentrantLock 在某些方面相较于 synchronized 有突出的优势。

ReentrantLock 的加锁方式分为两种:

  1. lock():如果获取不到锁,线程就会进入阻塞等待状态
  2. tryLock():如果获取不到锁,就会放弃加锁,而不是进入阻塞等待状态

使用 ReentrantLock 的时候需要手动 unLock() 解锁,如果忘记了手动解锁这个操作,将会带来比较严重的后果。

所以为了解决有些时候忘记手动解锁的情况,往往需要借助 try-finally 来进行解锁操作。

ReentrantLock lock = new ReentrantLock();lock.lock();try {    //working}finally {    lock.unlock();}

ReentranLock 与 synchronized 相比的优势

  1. ReentrantLock 在加锁的时候,有两种方式,lock() 和tryLock()。使用 lock() 的时候,如果未获取到锁就会进入阻塞等待状态,而使用 tryLock() 的话,如果没有获取到锁就会放弃获取,而不是进入阻塞等待

  2. ReentrantLock 提供了公平锁的实现(默认情况下是非公平锁)

  3. ReentranLock 提供了更强大的等待通知机制,搭配了Condition类,实现等待通知,可以指定唤醒某个线程

如何选择使用哪个锁?

  • 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.
  • 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.
  • 如果需要使用公平锁, 使用 ReentrantLock.

通常情况下还是建议使用 synchronized,虽然 ReentrantLock 在某些方面具有优势,但是使用起来较麻烦,并且Java程序员在 synchronized 里面做了很多的优化

原子类

原子类内部都是使用 CAS 操作实现的,因为 CAS 操作时原子性的,不需要进行加锁操作,所以性能要比加锁好很多。

  • AtomicBoolean
  • AtomicInteger
  • AtomicIntegerArray
  • AtomicLong
  • AtomicReference
  • AtomicStampedReference

原子类常见方法:

  • addAndGet(int delta); —— i += delta;
  • decrementAndGet(); —— --i;
  • getAndDecrement(); —— i–;
  • incrementAndGet(); —— ++i;
  • getAndIncrement(); —— i++;

CAS 操作我在前面详细讲解过,大家想要了解的话可以去看看。CAS(Compare And Swap)操作

线程池

线程池是为了解决因线程频繁创建和销毁而造成的资源浪费问题。

使用线程池需要使用到 ExecutorServiceExecutors 两个类。

ExecutorService pool = Executors.newFixedThreadPool(10);pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});
  • ExecutorService 表示一个线程池实例.
  • Executors 是一个工厂类, 能够创建出几种不同风格的线程池.
  • ExecutorService 的 submit 方法能够向线程池中提交若干个任务

Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池.
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer。

这里面也涉及到了工厂模式,大家可以去看看这篇文章了解一下【JavaEE】多线程案例-线程池

信号量

信号量(Semaphore)是一种实现任务间通信的机制,可以用于实现任务之间的同步或临界资源的互斥访问。它通常被用于协助一组相互竞争的任务来访问临界资源。

信号量是一个非负整数,获取信号量的任务会将该整数减1。当信号量为0时,所有试图获取该信号量的任务都将处于阻塞状态。信号量的值代表积累下来的释放信号量操作的次数。

申请资源的操作被称为 P 操作,释放资源的操作被称为 V 操作。

信号量可以分为二值信号量和计数信号量两种。二值信号量只有一个消息队列,队列有两种状态:空或满。而计数信号量可以看做长度大于1的消息队列,用于计数。信号量的计数值表示还有多少个事件未被处理。当某个事件发生时,任务或中断会释放一个信号量(将信号量计数值加1);当需要处理某个事件时,任务或中断会取走一个信号量(将信号量计数值减1)。

加锁和解锁的操作就可以看成是二值信号量的操作,当加锁的时候信号量就为0,释放锁的时候信号量就为1.

在Java代码中,信号量相关的操作被封装在 Semaphore 类中,acquire() 方法表示申请资源,release() 方法表示释放资源。

public class Test2 {    public static void main(String[] args) throws InterruptedException {        Semaphore semaphore = new Semaphore(4);        semaphore.acquire();        System.out.println("获取资源");        semaphore.acquire();        System.out.println("获取资源");        semaphore.acquire();        System.out.println("获取资源");        semaphore.acquire();        System.out.println("获取资源");        semaphore.acquire();        System.out.println("获取资源");        semaphore.release();        System.out.println("释放资源");    }}

在这里插入图片描述

当申请的资源量大于总的资源量的时候,线程就会进入阻塞等待状态,直到其他线程释放掉部分信号量。

CountDownLatch

CountDownLatch是Java中的一个同步工具类,用来协调多个线程之间的同步。它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。

CountDownLatch通过一个计数器来实现这个功能,计数器的初始值通常设置为需要等待完成的线程数量。每当一个线程完成了自己的任务,计数器的值就会减1。当计数器的值达到0时,表示所有线程都已经完成任务,此时在CountDownLatch上等待的线程就可以恢复执行。

CountDownLatch可以用来确保某些活动在其他活动完成之前不会继续执行。例如,可以确保某个计算在其需要的所有资源都被初始化之后才继续执行,或者确保某个服务在其依赖的所有其他服务都已经启动之后才启动。

在Java中,可以使用CountDownLatch的countDown()方法来对计数器做减操作,就是告诉CountDownLatch我这个当前的任务完成了,使用await()方法等待计数器达到0。所有调用await()方法的线程都会被阻塞,直到计数器达到0或者等待线程被中断或者超时。

public class Demo2 {    private static int count;    public static void main(String[] args) throws InterruptedException {        CountDownLatch countDownLatch = new CountDownLatch(10);        for(int i = 0; i < 10; i++) {            Thread t = new Thread(() -> {                try {                    Thread.sleep((long)(Math.random() * 4000));                    System.out.println(++count + "号完成比赛");                    countDownLatch.countDown();                } catch (InterruptedException e) {                    throw new RuntimeException(e);                }            });            t.start();        }        countDownLatch.await();        System.out.println("结束比赛");    }}

在这里插入图片描述
只有调用了10次 countDown() 方法之后,await() 方法才会结束等待,继续执行后面的代码。

需要注意的是,CountDownLatch是一次性的,一旦计数器的值达到0,就不能再次使用。如果需要多次使用类似的功能,可以考虑使用CyclicBarrier等其他同步工具类。

相关面试题

1) 线程同步的方式有哪些?

synchronized, ReentrantLock, Semaphore 等都可以用于线程同步.

2) 为什么有了 synchronized 还需要 juc 下的 lock?

以 juc 的 ReentrantLock 为例,

  • synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活,
  • synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃.
  • synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式.
  • synchronized 是通过 Object 的 wait / notify 实现等待-唤醒. 每次唤醒的是一个随机等待的线程. ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.

3) AtomicInteger 的实现原理是什么?

基于 CAS 机制. 伪代码如下:

class AtomicInteger {private int value;public int getAndIncrement() {int oldValue = value;while ( CAS(value, oldValue, oldValue+1) != true) {oldValue = value;}return oldValue;}}

4) 信号量听说过么?之前都用在过哪些场景下?

信号量, 用来表示 “可用资源的个数”. 本质上就是一个计数器.

使用信号量可以实现 “共享锁”, 比如某个资源允许 3 个线程同时使用, 那么就可以使用 P 操作作为加锁, V 操作作为解锁, 前三个线程的 P 操作都能顺利返回, 后续线程再进行 P 操作就会阻塞等待,直到前面的线程执行了 V 操作

来源地址:https://blog.csdn.net/m0_73888323/article/details/133488166

--结束END--

本文标题: 【JavaEE】JUC(Java.util.concurrent)常见类

本文链接: https://lsjlt.com/news/427464.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 【JavaEE】JUC(Java.util.concurrent)常见类
    文章目录 前言ReentrantLock原子类线程池信号量CountDownLatch相关面试题 前言 经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安...
    99+
    2023-10-10
    java java-ee 信号量 ReentrantLock 多线程 CountDownLatch
  • JUC包(java.util.concurrent)下的常用子类
    文章目录 前言一、对象锁juc.locks包二、原子类三、四个常用工具类3.1 信号量 Semaphore3.2 CountDownLatch 总结 前言 博主个人社区:开发与算...
    99+
    2023-09-14
    java jvm 面试 java-ee
  • Java中JUC包(java.util.concurrent)下的常用子类
    目录一、对象锁juc.locks包二、原子类三、四个常用工具类3.1 信号量 Semaphore3.2 CountDownLatch总结一、对象锁juc.locks包 在Java中除...
    99+
    2022-12-20
    java util类 java.util包中的类 juc包有哪些常用的
  • 【JavaEE】多线程(二)Thread 类及常见方法
    ✨哈喽,进来的小伙伴们,你们好耶!✨ 🛰️🛰️系列专栏:【JavaEE】 ✈️✈️本篇内容:Thread类再剖析! 🚀🚀代码存放仓库gitee:JavaEE初阶代码存...
    99+
    2023-09-04
    java-ee java 开发语言
  • java多线程JUC常用辅助类详解
    1.countDownLatch 减法计数器:实现调用几次线程后,在触发另一个任务 简单代码实现: 举例说明:就像五个人在同一房间里,有一个看门的大爷,当五个人都出去后,他才能锁门...
    99+
    2024-04-02
  • mysql 常见锁类型
    表锁 & 行锁 在 MySQL 中锁的种类有很多,但是最基本的还是表锁和行锁:表锁指的是对一整张表加锁,一般是 DDL 处理时使用,也可以自己在 SQL 中指定;而行锁指的是锁定某一行数据或某几行,或行和行之间的间隙。行锁的加锁方法比较复...
    99+
    2023-09-24
    mysql 数据库
  • Python常见异常类型处理
    目录1.Python语法错误 2.Python运行时错误 前言: 开发人员在编写程序时,难免会遇到错误,有的是编写人员疏忽造成的语法错误,有的是程序内部隐含逻辑问题造成的数据错误,还...
    99+
    2024-04-02
  • python常见错误类型
    Python标准异常总结 AssertionError 断言语句(assert)失败 AttributeError 尝试访问未知的对象属性 EOFError 用户输入文件末尾标志EOF(Ctrl+d) Floating...
    99+
    2023-01-31
    错误 常见 类型
  • 常见的MySQL锁类型
    MySQL 中常见的锁类型,需要具体代码示例导言:在数据库中,当多个客户端同时对同一数据进行读取或修改时,会出现并发操作的问题。为了保证数据的一致性和完整性,数据库引擎采用了锁机制来控制对共享数据的访问。MySQL 作为一种常用的关系型数据...
    99+
    2023-12-21
    - 行锁 - 读锁 (共享锁) - 写锁 (排他锁)
  • java常见异常类型有哪些
    Java常见的异常类型包括:1. NullPointerException(空指针异常):当引用变量为null时,使用该变量进行操作...
    99+
    2023-09-27
    java
  • MySQL 5.7常见数据类型
    ——《深入浅出MySQL(第二版)》笔记 数值类型 整数类型 字节 最小值 ...
    99+
    2024-04-02
  • 常见证书类型转换
    常见证书格式转换 常见的证书类型转换 PFX转JKS 找到jdk路径,使用命令行工具 cmd到jdk的bin下 2.执行keytool命令 keytool -importkeystore -srcke...
    99+
    2023-09-18
    服务器 https 运维
  • TypeScript常见类型有哪些
    小编给大家分享一下TypeScript常见类型有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!常见类型(Everyday Types)类型可以出现在很多地方,...
    99+
    2023-06-29
  • 常见云服务器类型
    云服务器(Cloud Drive)是一种云计算服务,它允许用户将数据从一台物理服务器快速地转移到另一台云服务器上,这通常涉及数据同步和备份。云服务器通常提供以下类型的云服务: SaaS云:这种云服务提供一种按需购买的方式,用户通过云服务...
    99+
    2023-10-27
    常见 类型 服务器
  • 在java中常见的异常类有哪些
    java中常见的异常类有:NullPointerException:当应用程序试图访问空对象时,则抛出该异常。SQLException:提供关于数据库访问错误或其他错误信息的异常。IndexOutOfBoundsException:指示某排...
    99+
    2024-04-02
  • python常见异常分类与处理方法
    Python常见异常类型大概分为以下类:    1.AssertionError:当assert断言条件为假的时候抛出的异常    2.AttributeError:当访问的对象属性不存在的时候抛出的异常    3.IndexError:超...
    99+
    2023-01-31
    异常 常见 方法
  • 常见安全类问题汇总
    问题编号: 3385 问题主题: 思科nbar的一些疑惑 提问者: samelv 提问时间: 2006-3-29 10:32:37 提问内容: 1、数据包识别率的问题 nb...
    99+
    2023-01-31
    常见
  • CSS常见的类选择器类型有哪些
    这篇“CSS常见的类选择器类型有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“CSS常...
    99+
    2024-04-02
  • C++ 函数返回值类型的常见类型
    c++++ 函数返回类型包括:void(无返回值)、基本类型(整数、浮点数、字符和布尔值)、指针、引用、类和结构。选择时,应考虑功能、效率和接口。如计算阶乘的 factorial 函数,...
    99+
    2024-04-12
    函数 返回值 c++
  • oracle常见故障类别有哪些
    这篇文章给大家分享的是有关oracle常见故障类别有哪些的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、数据库高可用性的几个目标    ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作