【JavaEE初阶】 JUC(java.util.concurrent) 的常见类

news/2024/5/20 10:19:57 标签: java, java-ee, JUC, 计算机操作系统, 多线程

文章目录

  • 🍀ReentrantLock
    • 🚩ReentrantLock 的用法
    • 🚩ReentrantLock 和 synchronized 的区别
    • 🚩如何选择使用哪个锁?
  • 🎍原子类
  • 🎋线程池
  • 🌳信号量 Semaphore
  • 🌴CountDownLatch
  • 🎄相关面试题
  • ⭕总结

🍀ReentrantLock

ReentrantLock是可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全.

我们从字面意思上就可以知道 “Reentrant” 这个单词的原意就是 "可重入”,

所以ReentrantLock 也是可重入锁.

🚩ReentrantLock 的用法

  • lock(): 加锁, 如果获取不到锁就死等.

  • trylock(超时时间): 加锁, 如果获取不到锁, 等待一定的时间之后就放弃加锁.

  • unlock(): 解锁

代码示例如下:

java">ReentrantLock lock = new ReentrantLock();
-----------------------------------------
lock.lock();
try {
	// working
} finally {
	lock.unlock()
}

🚩ReentrantLock 和 synchronized 的区别

  • synchronized 是一个关键字, 是 JVM 内部实现的(大概率是基于 C++ 实现).ReentrantLock 是标准库的一个类, 在 JVM 外实现的(基于 Java 实现).

  • synchronized 使用时不需要手动释放锁. ReentrantLock 使用时需要手动释放. 使用起来更灵活,但是也容易遗漏 unlock.

  • synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时间就放弃.

  • synchronized 是非公平锁, ReentrantLock 默认是非公平锁. 可以通过构造方法传入一个 true 开启公平锁模式.
    下面是 ReentrantLock 的构造方法,我们从中也可以看出这是一个公平锁(先来后到为公平)

java">/ ReentrantLock 的构造方法
public ReentrantLock(boolean fair) {
	sync = fair ? new FairSync() : new NonfairSync();
}
  • 更强大的唤醒机制.
    synchronized 是通过 Object 的 wait / notify 实现等待-唤醒.每次唤醒的是一个随机等待的线程.
    ReentrantLock 搭配 Condition 类实现等待-唤醒, 可以更精确控制唤醒某个指定的线程.

🚩如何选择使用哪个锁?

在实际应用中我们该如何选择锁呢?

  • 锁竞争不激烈的时候, 使用 synchronized, 效率更高, 自动释放更方便.

  • 锁竞争激烈的时候, 使用 ReentrantLock, 搭配 trylock 更灵活控制加锁的行为, 而不是死等.

  • 如果需要使用公平锁, 使用 ReentrantLock

🎍原子类

原子类内部用的是 CAS 实现,所以性能要比加锁实现 i++ 高很多。原子类有以下几个

  • AtomicBoolean

  • AtomicInteger

  • AtomicIntegerArray

  • AtomicLong

  • AtomicReference

  • AtomicStampedReference

这里以 AtomicInteger 举例,常见方法有:

java">addAndGet(int delta);   i += delta;
decrementAndGet();   --i;
getAndDecrement();   i--;
incrementAndGet();   ++i;
getAndIncrement();   i++;

🎋线程池

关于线程池这儿的内容大家可以去博主下面的这篇文章进行了解

【JavaEE初阶】 线程池详解与实现

🌳信号量 Semaphore

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

这里博主给大家举个例子,方便大家理解;

可以把信号量想象成是停车场的展示牌: 当前有车位 100 个. 表示有 100 个可用资源.
当有车开进去的时候, 就相当于申请一个可用资源, 可用车位就 -1 (这个称为信号量的 P 操作)
当有车开出来的时候, 就相当于释放一个可用资源, 可用车位就 +1 (这个称为信号量的 V 操作)
如果计数器的值已经为 0 了, 还尝试申请资源, 就会阻塞等待, 直到有其他线程释放资源

由于Semaphore 的 PV 操作中的加减计数器操作都是原子的, 可以在多线程环境下直接使用.

代码示例如下:

  • 创建 Semaphore 示例, 初始化为 4, 表示有 4 个可用资源.

  • acquire 方法表示申请资源(P操作), release 方法表示释放资源(V操作)

  • 创建 20 个线程, 每个线程都尝试申请资源, sleep 1秒之后, 释放资源. 观察程序的执行效果.

实现代码入下:

java">import java.util.concurrent.Semaphore;

public class ThreadDemo3 {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(4);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("申请资源");
                    semaphore.acquire();
                    System.out.println("我获取到资源了");
                    Thread.sleep(1000);
                    System.out.println("我释放资源了");
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 20; i++) {
            Thread t = new Thread(runnable);
            t.start();
        }
    }
}

🌴CountDownLatch

CountDownLatch的作用为:同时等待 N 个任务执行结束

就好像跑步比赛,10个选手依次就位,哨声响才同时出发;
但是每个选手的实力我们不知道,不知道谁最后到达
而我们需要所有选手都通过终点,才能公布成绩
CountDownLatch就可以起到等待他们全部跑完的效果

接下来我们依旧用代码实例进行讲解:

  • 构造 CountDownLatch 实例, 初始化 10 表示有 10 个任务需要完成.

  • 每个任务执行完毕, 都调用 latch.countDown() . 在 CountDownLatch 内部的计数器同时自减.

  • 主线程中使用 latch.await(); 阻塞等待所有任务执行完毕. 相当于计数器为 0

代码示例如下:

java">import java.util.concurrent.CountDownLatch;

public class ThreadDemo4 {
    public static void main(String[] args) throws Exception {
        CountDownLatch latch = new CountDownLatch(10);
        Runnable r = new Runnable() {
            public void run() {
                try {
                    Thread.sleep((long) (Math.random() * 10000));
                    latch.countDown();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        for (int i = 0; i < 10; i++) {
            new Thread(r).start();
        }
        // 必须等到 10 人全部回来
        latch.await();
        System.out.println("比赛结束");
    }
}

🎄相关面试题

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

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

  1. 为什么有了 synchronized 还需要 juc 下的 lock?

以 juc 的 ReentrantLock 为例,

  • synchronized 使用时不需要手动释放锁. ReentrantLock使用时需要手动释放. 使用起来更 灵活,

  • synchronized 在申请锁失败时, 会死等. ReentrantLock 可以通过 trylock 的方式等待一段时 间就放弃.

  • synchronized 是非公平锁, ReentrantLock 默认是非公平锁.可以通过构造方法传入一个 true 开启公平锁模式.

  • synchronized 是通过 Object 的 wait / notify实现等待-唤醒. 每次唤醒的是一个随机等待的 线程. ReentrantLock 搭配 Condition 类实现等待-唤醒,可以更精确控制唤醒某个指定的线程.

  1. AtomicInteger 的实现原理是什么?

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

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

执行过程参考博主写的【JavaEE初阶】 CAS详解

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

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

  1. 解释一下 ThreadPoolExecutor 构造方法的参数的含义

具体实现可以去博主所写的【JavaEE初阶】 线程池详解与实现进行查看

⭕总结

关于《【JavaEE初阶】 JUC(java.util.concurrent) 的常见类》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!


http://www.niftyadmin.cn/n/5125194.html

相关文章

Qt触摸屏双指缩放和单指移动界面(支持嵌入式设备)

本文介绍的QGraphicsView的双指缩放&#xff0c;QWidget更简单&#xff0c;可以参考当前内容。 方法一&#xff1a;&#xff08;QTouchEvent事件实现&#xff09; 使用场景&#xff1a;适用于paintevent绘制下的界面。 优点&#xff1a;不需要代码设置中心锚点&#xff08;锚点…

MySQL 高级函数整理

目录 MySQL 高级函数VERSIONIFCASE参考文章 MySQL 高级函数 函数描述BIN返回数字的二进制表示BINARY将值转换为二进制字符串CASE遍历条件并在满足第一个条件时返回一个值CAST将&#xff08;任何类型的&#xff09;值转换为指定的数据类型COALESCE返回列表中的第一个非空值CONN…

mariadb主主

服务器环境 ip1:10.10.0.11 ip2:10.10.0.12 1.分别导入配置文件 mkdir -p /data/mariadb/conf/ vim my.cnf [client] default-character-set utf8mb4[mysql] default-character-set utf8mb4[mysqld] lower_case_table_names1 wait_timeout1800 max_allowed_packet 512M …

github中.gitignore不起作用啦

文章目录 前言两种方法解决清除本地缓存设置不需要 额外注意 前言 提示&#xff1a;人不是靠讲话来生活。每个人都应该靠行动。而行动&#xff0c;是需要时间来证明的。 --《自在独行》 两种方法解决 清除本地缓存 (.gitignore中已经表标明忽略的文件目录下的文件了&#xf…

程序生活 - 减肥小记

文章目录 缘起健康就好了吗&#xff1f;关于外在和物质生活难与易 我的减肥生活一些细节轻断食戒糖、油炸、重口味睡眠改变社交方式用运动化解压力不喝牛奶 缘起 2017年的一次腿受伤&#xff0c;让我从一个怎么都吃不胖的人&#xff0c;变成了一个实实在在的胖子。 如果你从来…

JAVA商城和PHP商城的区别

JAVA商城和PHP商城系统都是目前市场上较为流行的电商平台系统&#xff0c;它们都有自己独特的优势和适用场景&#xff0c;下面就详细介绍一下它们的差异和特点。 一、JAVA商城系统 JAVA商城系统&#xff0c;顾名思义&#xff0c;是使用JAVA语言开发的一种电子商务平台系统。它…

YModem协议总结

《YModem协议总结》 目录 第1章 YModem协议简介 4 1.1 基本介绍 4 1.2 YModem基本介绍 4 第2章 YModem传输协议 5 2.1 起始帧的数据格式 5 2.2 数据帧的数据格式 5 2.3 结束帧数据结构 6 2.4 文件传输过程 6 2.5 CRC的计算 7 附录A 附录 8 A.1 附录 8 第1章 YModem协议简…

Windows客户端下pycharm配置跳板机连接内网服务器

问题&#xff1a;实验室服务器仅限内网访问&#xff0c;无法在宿舍&#xff08;外网&#xff09;访问实验室的所有内部服务器&#xff0c;但同时实验室又提供了一个外网可以访问的跳板机&#xff0c;虽然可以先ssh到跳板机再从跳板机ssh到内网服务器&#xff0c;但这种方式不方…