JUC下的ThreadLocalRandom详解

ThreadLocalRandom 是Java并发包(java.util.concurrent)中提供的一个随机数生成器类,它是从Java 7开始引入的。相较于传统的Math.random()Random类,ThreadLocalRandom更适用于多线程环境,因为它为每个线程维护了一个独立的随机数生成器实例,减少了线程之间的竞争,提高了性能。

详细介绍:

背景与目的

在讨论ThreadLocalRandom之前,首先需要理解它所解决的问题背景。在多线程编程环境中,传统随机数生成器类如java.util.Random在并发使用时面临线程安全问题。尽管Random类的方法(如nextInt())是线程安全的,但由于其内部状态(种子值)的更新需要同步控制,这在高并发环境下会导致性能瓶颈。为了解决这一问题,Java 7引入了ThreadLocalRandom,旨在为每个线程提供独立的随机数生成器,从而减少线程间资源的竞争,提高效率。

设计原理

ThreadLocalRandom的核心设计基于Java的ThreadLocal类。ThreadLocal是一种线程绑定的变量,每个线程都持有一个独立的变量副本,因此,在不同线程中对这个变量的操作互不影响。ThreadLocalRandom正是利用了这一点,为每个线程提供了一个单独的随机数生成器实例,这些实例通过ThreadLocal存储,保证了线程间的隔离性,同时也避免了同步开销。

获取实例

获取ThreadLocalRandom的实例非常直接,只需调用静态方法ThreadLocalRandom.current()。这个方法会自动为当前线程(如果尚未分配)创建一个ThreadLocalRandom实例,并返回之。由于使用了ThreadLocal,所以每次调用都是线程安全的,同时确保了高效性。

功能与方法

ThreadLocalRandom提供了丰富的方法来生成不同类型的随机数,包括但不限于整数、长整数、浮点数以及特定范围内的随机数。这些方法覆盖了基本的随机数需求,例如:

  • nextInt():生成下一个整型随机数。
  • nextInt(int bound):生成一个位于[0, bound)区间内的随机整数。
  • nextLong():生成下一个长整型随机数。
  • nextDouble():生成下一个位于[0.0, 1.0)区间的双精度浮点数。
  • 还有更多高级方法,如生成布尔值、随机选择数组元素等。
性能优势
  • 减少锁竞争:因为每个线程有自己的随机数生成器实例,所以在高并发场景下,避免了因争夺共享资源(如Random实例)而产生的锁竞争,大大提高了性能。
  • 高效内存使用:每个线程只保留一个随机数生成器实例,相比每个操作都新建对象的方式,内存占用更少,垃圾回收压力减轻。
  • 简化编程模型:开发者无需手动处理同步问题,使用起来更加简便。
与其他随机数生成器的对比
  • 相较于Math.random()ThreadLocalRandom不仅提供了更好的并发性能,还提供了更灵活的随机数生成选项。
  • Random相比,主要区别在于并发性能和使用场景。Random适合单线程或需要跨线程共享随机序列的场景,而ThreadLocalRandom专为多线程设计,提供更高的并发效率。

  ThreadLocalRandom是Java并发编程中一个重要的工具,尤其在需要在多线程环境中高效生成随机数的场景下。它通过线程局部变量的设计,有效避免了高并发下的性能瓶颈,是现代并发程序设计中不可或缺的一部分。然而,选择使用ThreadLocalRandom还是其他随机数生成器,应当基于具体的应用场景和性能要求来决定。

使用场景

高并发服务中的唯一标识生成

在分布式系统或者高并发的Web服务中,经常需要生成唯一的序列号、订单号、会话ID等。由于ThreadLocalRandom在多线程环境下的高性能表现,它可以作为生成这些唯一标识的基础组件。通过结合时间戳、机器标识等信息,可以确保在每个线程中快速且安全地生成不重复的ID。

并行数据处理与采样

在大数据处理或并行计算框架(如Apache Spark、Hadoop)中,当需要对海量数据进行随机抽样、洗牌(shuffle)或其他随机化操作时,ThreadLocalRandom能够确保每个工作线程独立高效地执行随机操作,避免了全局随机数生成器带来的线程竞争问题,从而加速数据处理流程。

游戏与模拟系统

游戏开发中,尤其是在需要大量随机事件模拟的场景下,如角色属性初始化、道具掉落、地图生成等,使用ThreadLocalRandom可以提供稳定的随机数生成能力,且不会因为多线程处理玩家请求而降低性能。此外,模拟系统如经济模型预测、交通流量模拟等,也经常依赖高效的随机数生成来模拟真实世界的不确定性。

A/B测试与算法实验

在线服务进行A/B测试或运行算法实验时,需要对用户流量进行随机分配,以评估不同方案的效果。利用ThreadLocalRandom可以在高并发的用户请求中迅速做出随机决策,确保实验分组的随机性和公平性,同时保持系统的响应速度。

并发编程教育与研究

在教授并发编程课程或进行相关研究时,ThreadLocalRandom作为一个典型的线程局部变量应用案例,常被用来演示如何在多线程环境中正确且高效地管理共享资源。通过实践操作,学习者可以深刻理解线程局部存储的概念及其在解决并发问题上的优势。

性能敏感的随机数应用

对于任何对性能有严格要求且需要频繁生成随机数的应用,如金融风险模型模拟、高性能计算中的随机算法等,ThreadLocalRandom都能提供比传统随机数生成器更优的性能表现,是首选的随机数生成工具。

ThreadLocalRandom适用于所有需要在多线程环境下高效执行随机操作的场景,尤其是在并发度高、对性能敏感的系统和服务中,它能够显著提升应用的响应速度和吞吐量。

实际开发中的使用详情与注意事项

使用详情及简单示例
  1. 获取实例: 在Java中,直接通过ThreadLocalRandom.current()方法获取当前线程的ThreadLocalRandom实例,无需手动创建。这个方法保证了每个线程都拥有自己的随机数生成器实例,减少了线程间的竞争。

  2. 基本操作: ThreadLocalRandom提供了丰富的API用于生成不同类型的随机数,包括整数(nextInt, nextLong)、浮点数(nextDouble, nextFloat)以及范围内的随机数(nextInt(int bound), nextLong(long bound)等)。这些方法可以直接用于各种随机需求。

  3. 分布与随机性: 对于需要特定分布的随机数,如均匀分布、泊松分布、正态分布等,虽然ThreadLocalRandom本身不直接提供这些高级功能,但可以通过基本的随机数转换得到。例如,可以通过Box-Muller变换从均匀分布生成正态分布的随机数。

  4. 并行处理: 在并行处理数据时,每个工作线程应该使用自己的ThreadLocalRandom实例进行随机操作,这样可以最大限度地减少锁的竞争,提高效率。特别是在使用多线程框架处理大量数据时,这一点尤为重要。

  5. 资源消耗: 相较于全局的Random类,ThreadLocalRandom减少了同步开销,因此在高并发场景下更加高效。但是,如果系统线程数量非常大,每个线程维护一个随机数生成器实例可能会增加内存消耗,虽然这种影响通常很小。

初始化与获取实例

在Java中,使用ThreadLocalRandom不需要显式地创建实例。它通过静态方法ThreadLocalRandom.current()自动为每个线程提供一个单独的随机数生成器实例。这意味着每个线程都有独立的随机数序列,消除了多线程环境下的竞争问题。示例代码如下:

ThreadLocalRandom currentRandom = ThreadLocalRandom.current();
生成整数随机数
  • 无界随机数:生成一个任意整数。

    int randomInt = currentRandom.nextInt();
  • 有界随机数:生成一个指定范围内的随机整数,包括最小值但不包括最大值。

    int randomBoundInt = currentRandom.nextInt(upperBound); // 生成[0, upperBound)之间的随机数
    int randomRangeInt = currentRandom.nextInt(min, max); // Java中此方法不存在,需手动计算

对于生成指定区间内的随机数,由于nextInt方法不直接支持两个参数指定范围,可以这样实现:

int randomInRange = min + currentRandom.nextInt(max - min);
生成长整型随机数

与整数类似,可以生成无界或有界的长整型随机数。

long randomLong = currentRandom.nextLong();
long randomBoundLong = currentRandom.nextLong(upperBound);
生成浮点数
  • 0.0到1.0之间的double

    double randomDouble = currentRandom.nextDouble();
  • 指定范围内的double:虽然没有直接的方法生成指定范围的double,但可以通过生成0到1之间的数,然后缩放和平移来实现。

double randomScaledDouble = min + (max - min) * currentRandom.nextDouble();
高级使用
  • 序列生成:虽然ThreadLocalRandom本身不直接支持生成序列号,但你可以基于它的随机数生成器来实现自定义的序列生成逻辑,比如生成不重复的ID。

  • 随机选取:在集合中随机选择元素,可以通过生成随机索引来实现。

List<String> list = ...; // 假设这是你的列表
int randomIndex = currentRandom.nextInt(list.size());
String randomElement = list.get(randomIndex);
性能考量

由于ThreadLocalRandom避免了多线程间对共享资源的竞争,因此在高并发环境下性能优越。特别是在执行大量随机数生成操作时,相比使用Math.random()或全局Random对象,它能显著降低同步开销,提升程序效率。

ThreadLocalRandom的使用主要围绕其提供的丰富API展开,包括生成不同类型和范围的随机数。在实际开发中,根据具体需求选择合适的方法,并结合线程安全性和性能要求来灵活应用。由于它为每个线程提供独立的随机数生成器,因此特别适合多线程环境下的并发随机数需求。

注意事项
  1. 线程泄漏: 虽然ThreadLocalRandom与线程绑定,但如果线程池复用线程,且任务中使用了ThreadLocalRandom后没有清理,可能会导致线程本地变量累积,从而潜在地引起内存泄漏。虽然这种情况较为罕见,但在设计长时间运行的服务时应考虑这一点。

  2. 非线程安全的操作: 尽管ThreadLocalRandom自身是线程安全的,但在使用其生成的随机数进行其他操作时,仍然需要注意操作的数据结构或资源是否线程安全。例如,多个线程使用随机数更新同一个共享变量时,仍需外部同步机制。

  3. 性能考量: 在极少数情况下,如果应用的线程数量极其庞大,且随机数生成并非性能瓶颈,使用全局的Random类(通过适当的同步机制)可能在内存占用上更为高效。不过,这需要权衡性能与资源消耗。

  4. 测试与调试: 在单元测试和系统调试过程中,应注意ThreadLocalRandom的行为可能随线程的不同而异,这可能导致某些测试难以复现或调试困难。可以考虑在测试环境中使用固定种子的Random来确保可重复性。

  5. 版本兼容性: 虽然ThreadLocalRandom自Java 7起就被引入,但在使用较旧的Java版本时,需要检查是否支持,并注意相应的API差异。

ThreadLocalRandom是处理并发随机数生成的理想选择,但开发者应当了解其特性,合理设计使用模式,并注意潜在的副作用,以确保系统的稳定性和效率。

优缺点

优点
  1. 线程安全性: ThreadLocalRandom 最显著的优点在于它的线程安全性设计。每个线程都维护自己的随机数生成器实例,这从根本上消除了多线程环境下对共享资源(如传统的 Random 类实例)的访问冲突和同步开销。这对于高并发应用来说,能显著提升性能。

  2. 性能高效: 由于每个线程都有独立的随机数生成器,避免了线程间的竞争和锁的使用,使得在多线程环境下生成随机数的效率非常高。对于需要频繁生成随机数的应用场景,这种设计可以减少线程上下文切换和同步等待时间,从而提高整体的吞吐量。

  3. 易于使用: 尽管提供了高级的线程局部性功能,ThreadLocalRandom 的使用却相当简单。开发者只需要调用 ThreadLocalRandom.current() 即可获得当前线程的随机数生成器实例,之后就可以像使用普通 Random 实例一样调用各种生成随机数的方法。

  4. 灵活性: 提供了生成不同数据类型(整型、长整型、双精度浮点型等)和范围随机数的能力,满足了大多数随机数生成的需求。同时,用户可以根据生成的随机数进一步构造更复杂的随机逻辑或算法。

缺点
  1. 初始化延迟: 第一次在每个线程中调用 ThreadLocalRandom.current() 时,会为该线程初始化一个 ThreadLocalRandom 实例。虽然这个过程很快,但如果应用程序创建了大量的线程,尤其是在短时间内,可能会观察到轻微的性能影响。

  2. 资源占用: 每个线程持有独立的 ThreadLocalRandom 实例意味着会占用更多的内存资源,尤其是在拥有成千上万个线程的极端情况下。尽管现代应用通常不会遇到这样的问题,但在资源受限的环境中仍需考虑。

  3. 不适用于全局唯一序列: 虽然 ThreadLocalRandom 在多线程环境下表现出色,但它并不适合生成全局唯一的序列号或ID,因为每个线程生成的随机数序列是独立的,无法保证全局唯一性。

  4. 缺乏特定的高级功能: 相比于一些专门的随机数生成库,ThreadLocalRandom 提供的功能较为基础,比如它不直接支持生成符合特定概率分布(如正态分布、泊松分布)的随机数,如果需要这些高级功能,可能需要额外的数学转换或寻找其他库支持。

ThreadLocalRandom 是Java中处理多线程随机数生成需求的一个高效且实用的工具,尤其适合那些对性能有较高要求的应用场景。然而,在特定场景下,也需要注意其潜在的资源消耗和功能局限。

Java代码示例

基础使用示例

首先,让我们看一个简单的例子,展示如何使用ThreadLocalRandom生成一个介于两个值之间的随机整数。

import java.util.concurrent.ThreadLocalRandom;

public class ThreadLocalRandomExample {
    public static void main(String[] args) {
        // 生成一个介于10到20之间的随机整数
        int randomInt = ThreadLocalRandom.current().nextInt(10, 21);
        System.out.println("随机整数: " + randomInt);

        // 生成一个随机的长整数
        long randomLong = ThreadLocalRandom.current().nextLong();
        System.out.println("随机长整数: " + randomLong);

        // 生成一个介于0.0到1.0之间的随机浮点数
        double randomDouble = ThreadLocalRandom.current().nextDouble();
        System.out.println("随机浮点数: " + randomDouble);
    }
}
多线程环境示例

接下来,我们将展示在多线程环境下如何使用ThreadLocalRandom。在这个例子中,我们将创建几个线程,每个线程都会生成并打印自己的随机数,以此来演示ThreadLocalRandom的线程安全性。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ThreadLocalRandom;

public class MultiThreadedRandomExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5); // 创建一个固定大小的线程池

        for (int i = 0; i < 10; i++) {
            executor.submit(() -> {
                int threadId = Thread.currentThread().getId();
                int randomNum = ThreadLocalRandom.current().nextInt(100);
                System.out.println("线程 " + threadId + " 生成的随机数: " + randomNum);
            });
        }

        executor.shutdown(); // 关闭线程池
        try {
            executor.awaitTermination(1, TimeUnit.MINUTES); // 等待所有任务完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这个多线程示例中,每个线程通过ThreadLocalRandom.current()获取到的是各自独立的随机数生成器实例,因此即使多个线程同时运行,它们也不会互相干扰,确保了线程安全和高效运行。 

使用过程中可能遇到的问题及解决方案

在使用ThreadLocalRandom或任何Java中的随机数生成工具时,可能会遇到一些常见问题。以下是一些潜在问题及其解决方案:

1. 性能考量

问题: 在高并发环境下,如果每个线程频繁地调用ThreadLocalRandom.current(),理论上虽然对性能影响较小(因为ThreadLocalRandom是线程局部的),但极端情况下仍可能对性能有微妙影响。

解决方案: 尽量减少直接在循环内部调用ThreadLocalRandom的次数。可以预先生成一定数量的随机数存储起来,在需要时再从这个集合中取,这样可以减少直接调用的频率。

2. 随机性不足

问题: 如果对随机性的要求非常高,且生成的随机数范围较小,可能会发现生成的序列在短时间内有重复,尤其是在高频率调用时。

解决方案: 确保使用的随机区间足够大以增加多样性。对于需要高度随机性的应用,可以考虑使用更复杂的随机算法或者结合多种随机源。

3. 线程安全误解

问题: 虽然ThreadLocalRandom本身是线程安全的,但使用者可能错误地将其实例字段共享给多个线程,这可能导致意料之外的行为。

解决方案: 确保总是通过ThreadLocalRandom.current()来获取当前线程的随机数生成器实例,而不是将实例作为成员变量跨线程共享。

4. 随机数生成的可预测性

问题: 在某些安全敏感的应用场景中,如加密算法或安全令牌生成,使用默认的伪随机数生成器可能不够安全,因为它们的输出在理论上是可预测的。

解决方案: 对于这类需求,应该使用Java的安全随机数生成器SecureRandom,它是设计来满足加密安全级别的随机数需求。

5. 初始化问题

问题: 尽管罕见,但在某些非常特定的情况下(如应用程序在安全沙箱环境中运行),初始化ThreadLocalRandom可能会失败。

解决方案: 检查异常并采取适当的错误处理机制。尽管这种情况很少见,但在关键应用中编写健壮的错误处理代码总是好的做法。

6. 随机种子问题

问题: 如果需要复现随机数序列(比如在调试或测试时),默认的随机种子是基于系统时钟,这使得序列难以复现。

解决方案: 使用特定的种子初始化随机数生成器。虽然ThreadLocalRandom不直接支持设置种子,但可以在测试环境下使用其他可设置种子的随机类(如java.util.Random),并在需要的时候切换回ThreadLocalRandom

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【spring】application.yml导入额外配置文件

有时候application.yml 已经配置很多配置已经很大很乱了想把他们拆出去放在一个独立的XX.yml文件管理。这时候就用到了 spring.config.import 属性。 spring.config.import spring.config.import 是 Spring Boot 2.4 版本引入的一个配置属性&#xff0c;用于导入额外的配置数…

Java入门基础学习笔记21——Scanner

在程序中接收用户通过键盘输入的数据&#xff1a; 需求&#xff1a; 请在程序中&#xff0c;提示用户通过键盘输入自己的姓名、年龄、并能在程序中收到这些信息&#xff0c;怎么解决&#xff1f; Java已经写好了实现程序&#xff0c;我们调用即可。 API&#xff1a;Applicat…

解决 XXL-Job 端口额外占用问题 小结

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java技术栈笔记 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. 问题解决 2.1. 下载源码 2.2. 启动admin服务器 2.3. 项目引入c…

MySQL-InnoDB数据存储结构

1、存储结构-页 索引结构提供了高效的索引方式&#xff0c;索引信息以及数据记录都保存在数据文件或索引文件中&#xff08;本质存储在页结构中&#xff09; 1.1、磁盘与内存交互的基本单位&#xff1a;页 在InnoDB中将数据划分为若干页&#xff0c;页的默认大小为&#xff…

OpenCV-基于累计直方图的中值滤波算法

作者&#xff1a;翟天保Steven 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 实现原理 基于累计直方图的中值滤波算法是一种图像处理技术&#xff0c;用于去除图像中的噪声。它利用了像素值的频数分布&#…

LeetCode/NowCoder-链表经典算法OJ练习2

最好的&#xff0c;不一定是最合适的&#xff1b;最合适的&#xff0c;才是真正最好的。&#x1f493;&#x1f493;&#x1f493; 目录 说在前面 题目一&#xff1a;分割链表 题目二&#xff1a;环形链表的约瑟夫问题 SUMUP结尾 说在前面 dear朋友们大家好&#xff01;&…

卷积神经网络边缘识别

为什卷积神经网络能够识别图片呢&#xff1f;是基于图片相似度比较&#xff0c;两张图片的点击越大说明两张图片越像&#xff0c;比如我们那狗胡子的图片去比较&#xff0c;如果相似度很高&#xff0c;就是认为这个动物更像狗。点积越大&#xff0c;图片越相似&#xff0c;这个…

Windows2016系统禁止关闭系统自动更新教程

目录 1.输入cmd--适合系统2016版本2.输入sconfig&#xff0c;然后按回车键3.输入5&#xff0c;然后按回车键4.示例需要设置为手动更新&#xff0c;即输入M&#xff0c;然后按回车键 1.输入cmd–适合系统2016版本 2.输入sconfig&#xff0c;然后按回车键 3.输入5&#xff0c;然后…

揭秘APP广告变现:轻松赚取收益的秘密武器,你还在等什么?

在移动互联网时代&#xff0c;APP广告变现已成为许多开发者和公司获取收益的重要方式。它如同一把秘密武器&#xff0c;帮助那些掌握了其使用技巧的人轻松赚取收益。那么&#xff0c;究竟什么是APP广告变现&#xff1f;又如何通过它轻松赚取收益呢&#xff1f;接下来&#xff0…

对中介者模式的理解

目录 一、场景1、题目 【[来源](https://kamacoder.com/problempage.php?pid1094)】1.1 题目描述1.2 输入描述1.3 输出描述1.4 输入示例1.5 输出示例 二、不采用中介者设计模式1 代码2 问题 三、中介者设计模式1 代码2 更好的例子 四、个人思考 一、场景 设计模式不是银弹&am…

STM32(开篇总结)

STM32介绍 STM32是意法半导体公司基于ARM Cortex-M内核开发的32位微控制器 STM32常应用在嵌入式领域&#xff0c;如智能车、无人机、机器人、无线通信、物联网、工业控制、娱乐电子产品等 STM32功能强大、性能优异片上资源丰富、功耗低&#xff0c;是一款经典的嵌入式微控制器…

Lesson5--二叉树(超详细版)

【本节目标】 1. 树概念及结构 2. 二叉树概念及结构 3. 二叉树顺序结构及实现 4. 二叉树链式结构及实现 1.树概念及结构 1.1树的概念 树是一种 非线性&#xff08;线性结构就是顺序表链表&#xff09; 的数据结构&#xff0c;它是由 n &#xff08; n>0 &#xff09;个…

超详细比特币Brc-20部署发布:实用步骤演示,请点赞收藏!(一)

大家好&#xff0c;我是程序员大猩猩。 上一节我们讲到如何使用Remix部署智能合约到币安链&#xff0c;如下&#xff1a; 使用Remix部署智能合约到币安链&#xff08;Remix的操作介绍 币安链合约的部署&#xff09; 有很多小伙伴私信问我&#xff0c;那么BTC&#xff08;比特…

linux系统——文件系统挂载原理

linux中从根目录到下面文件&#xff0c;用一套文件系统&#xff0c;当插入外来磁盘&#xff0c;如u盘时&#xff0c;两套文件系统如何进行交互&#xff1f; 挂载即为将一个存储设备连接到另一个已经存在的文件夹中&#xff0c;访问这个文件夹&#xff0c;即为访问该设备存储内容…

如何将 DFMini player MP3 模块与 Arduino 结合使用

要创建此项目&#xff0c;您将使用&#xff1a; DFPlayer迷你MP3模块 10kΩ电阻 开关按钮 面包板 Arduino UNO 杜邦线 现在&#xff0c;我们将学习如何构建该项目。 什么是DF Mini Player MP3模块 DFMini Player 模块是一个小型音乐播放器。它成本低、功耗低&#xff0c;可…

类与对象(二)

封装 封装作为面向对象三大特性&#xff08;封装&#xff0c;继承&#xff0c;多态&#xff09;之一&#xff0c;那如何实现封装性的呢&#xff1f;就又得拿出上面的访问修饰限定符的图 public: 就是在任何地方都可以访问 protected: 涉及子类在介绍继承时详细介绍 default: …

【深度学习目标检测】二十六、基于深度学习的垃圾检测系统-含数据集、GUI和源码(python,yolov8)

设计垃圾检测系统的意义在于多个方面&#xff0c;这些方面不仅关乎环境保护和城市管理&#xff0c;还涉及到技术进步和社会效益。以下是设计垃圾检测系统的主要意义&#xff1a; 环境保护与资源回收&#xff1a; 垃圾检测系统能够有效地识别不同种类的垃圾&#xff0c;帮助人们…

数据可视化(十二):Pandas太阳黑子数据、图像处理——离散极值、核密度、拟合曲线、奇异值分解等高级操作

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

【JS红宝书学习笔记】第1、2章 初识JS

第1章 什么是JavaScript JavaScript 是一门用来与网页交互的脚本语言&#xff0c;包含以下三个组成部分。 ECMAScript&#xff1a;由 ECMA-262 定义并提供核心功能。文档对象模型&#xff08;DOM&#xff09;&#xff1a;提供与网页内容交互的方法和接口。浏览器对象模型&…

LeetCode 98. 验证二叉搜索树

LeetCode 98. 验证二叉搜索树 1、题目 题目链接&#xff1a;98. 验证二叉搜索树 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节…