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

在这里插入图片描述

文章目录

  • 前言
  • 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 操作

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

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

相关文章

QT 实现 TCP 客户端服务器代码

服务器 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include<QTcpServer> //服务器头文件 #include<QTcpSocket> //客户端头文件 #include<QList> //链表容器 #include<Q…

Vue2插槽的简单使用示例

插槽-slot 基本使用作用域插槽具名插槽 1. 基本使用 比较简单, 比如: <template> <a :href”url”> <slot :slotData”website”>xxxx</slot> </a> </template> 父页面使用--不用获取slotdemo组件里面的数据的时候: <slotDemo :u…

vue、vuex状态管理、vuex的核心概念state状态

每一个 Vuex 应用的核心就是 store&#xff08;仓库&#xff09;。“store”基本上就是一个容器&#xff0c;它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同&#xff1a; Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候&…

【TensorFlow Hub】:有 100 个预训练模型等你用

要访问TensorFlow Hub&#xff0c;请单击此处 — https://www.tensorflow.org/hub 一、说明 TensorFlow Hub是一个库&#xff0c;用于在TensorFlow中发布&#xff0c;发现和使用可重用模型。它提供了一种使用预训练模型执行各种任务&#xff08;如图像分类、文本分析等&#xf…

导入sklearn报错:No module named ‘threadpoolctl‘

前言 sklearn的安装 注意sklearn安装不是pip install sklearn&#xff0c;而是pip install scikit-learn; 使用清华源安装sklearn&#xff0c;windows中使用自己的python环境&#xff0c;在命令行输入如下&#xff1a; pip install scikit-learn -i https://pypi.tuna.tsing…

COCI2022-2023#1 Neboderi

P9032 [COCI2022-2023#1] Neboderi 题目大意 有一个长度为 n n n的序列 h i h_i hi​&#xff0c;你需要从中选择一个长度大于等于 k k k的子区间 [ l , r ] [l,r] [l,r]&#xff0c;使得 g ( h l h l 1 ⋯ h r ) g\times (h_lh_{l1}\cdotsh_r) g(hl​hl1​⋯hr​)最小&…

A : DS单链表--类实现

Description 用C语言和类实现单链表&#xff0c;含头结点 属性包括&#xff1a;data数据域、next指针域 操作包括&#xff1a;插入、删除、查找 注意&#xff1a;单链表不是数组&#xff0c;所以位置从1开始对应首结点&#xff0c;头结点不放数据 类定义参考 #include<…

计算机专业毕业设计项目推荐11-博客项目(Go+Vue+Mysql)

博客项目&#xff08;GoVueMysql&#xff09; **介绍****系统总体开发情况-功能模块****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设计流程以及模式&am…

[黑马程序员TypeScript笔记]------一篇就够了

目录&#xff1a; TypeScript 介绍 TypeScript 是什么&#xff1f;TypeScript 为什么要为 JS 添加类型支持&#xff1f;TypeScript 相比 JS 的优势TypeScript 初体验 安装编译 TS 的工具包 编译并运行 TS 代码 简化运行 TS 的步骤 TypeScript 常用类型 概述类型注解常用基础…

基于微信小程序的付费自习室

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 文章目录 1 简介2 技术栈3 需求分析3.1用户需求分析3.1.1 学生用户3.1.3 管理员用户 4 数据库设计4.4.1 E…

编写可扩展的软件:架构和设计原则

在今天的软件开发领域&#xff0c;可扩展性是一个至关重要的概念。无论您是开发一个小型应用程序还是一个大规模的软件系统&#xff0c;都需要考虑如何使您的软件能够在不断变化的需求下进行扩展和演进。本文将探讨编写可扩展软件的关键架构和设计原则&#xff0c;以帮助开发人…

Linux友人帐之账号用户管理

一、账号管理 1.1简介 Linux系统是一个多用户多任务的分时操作系统&#xff0c;任何一个要使用系统资源的用户&#xff0c;都必须首先向系统管理员申请一个账号&#xff0c;然后以这个账号的身份进入系统。 用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪&#…

lv7 嵌入式开发-网络编程开发 12 IP协议与ethernet协议

目录 1 IP协议作用和意义 2 IP数据报首部格式 3 IP数据报分片 4 以太网协议作用和意义&#xff08;链路层&#xff09; 5 练习 1 IP协议作用和意义 IP网的意义 当互联网上的主机进行通信时&#xff0c;就好像在一个网络上通信一样&#xff0c;看不见互连的各具体的网络异…

mysql面试题10:MySQL中有哪几种锁?表级锁、行级锁、页面锁区别和联系?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Mysql中有哪几种锁? 在MySQL中,主要有以下几种类型的锁: 共享锁(Shared Lock):也称为读锁。多个事务可以同时持有共享锁,可以读取但不能修…

浏览器渲染原理

1 浏览器是如何渲染页面的&#xff1f; 浏览器的渲染 render 将 html 字符串 --> 像素信息 function render(html) {// ...return pixels; }网络&#xff1a;拿 HTML&#xff08;JS和CSS也在HTML中&#xff09;&#xff1b; 渲染&#xff1a;渲染&#xff1b; 当浏览器的 …

c语言系统编程之多进程

程序与进程的区别&#xff1f; 程序是静态的未运行的二进制文件&#xff0c;存储在磁盘中 进程是已经运行的二进制文件&#xff0c;存储在内存中 进程的内存划分图有哪几部分&#xff1f; 堆&#xff08;存储malloc和calloc出来的空间&#xff09;、栈&#xff08;局部变量…

BFS模板:844. 走迷宫

给定一个 nmnm 的二维整数数组&#xff0c;用来表示一个迷宫&#xff0c;数组中只包含 00 或 11&#xff0c;其中 00 表示可以走的路&#xff0c;11 表示不可通过的墙壁。 最初&#xff0c;有一个人位于左上角 (1,1)(1,1) 处&#xff0c;已知该人每次可以向上、下、左、右任意…

Windows10实用的12个快捷组合键

Windows10实用的12个快捷组合键 1、网页多标签切换 CTRL TAB 2、恢复不小心关闭的标签页 CTRLSHIFT T 3、新建标签页 CTRL T 4、高亮选择地址栏 ALT D 5、打开设置 WIN I 6、打开任务管理器 CTRLSHIFT ESC 7、打开文件资源管理器 WIN E 8、黑屏或屏幕卡顿无响应&#x…

面试题: Spring AOP是如何实现的? 它和AspectJ有什么区别?

Spring AOP是如何实现的? 它和AspectJ有什么区别? 背景答案SpringAOP实现与AspectJ区别AOP 的几种实现方案 个人评价 背景 想换工作, 学习一下面试题, 算是学习记录的输出. 答案 SpringAOP实现 Spring AOP 是利用的动态代理的机制, 如果一个Bean 实现了接口, 那么就会采用…

国庆中秋宅家自省: Python在Excel中绘图尝鲜

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 Python3数据科学包系列(三):数据分析实战 【一】国庆中秋: 悟 【国…