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

Java ReentrantLock

目录

1 互斥性

2 公平性

3 可重入性

4 获取和释放锁

5 尝试获取锁

6 可中断的锁定

7 条件变量

8 性能

9 使用场景


ReentrantLock 是 Java 提供的一种可重入的互斥锁,位于 java.util.concurrent.locks 包中,它实现了 Lock 接口。这个锁提供了与内置监视器锁(通过 synchronized 关键字实现)类似的互斥性和内存可见性,但具有更强大的功能和灵活性。以下是 ReentrantLock 的一些基本概念:

1 互斥性

ReentrantLock 可以确保同一时间只有一个线程可以执行由该锁保护的代码块,从而避免了竞态条件。

2 公平性

  • 非公平锁:默认情况下,ReentrantLock 是非公平的。这意味着线程获取锁的顺序并不是按照它们请求锁的顺序来确定的。非公平锁通常提供更高的吞吐量。
  • 公平锁:当创建 ReentrantLock 实例时,可以通过传入 true 参数来指定为公平锁。公平锁保证线程将按照它们请求锁的顺序获得锁。这可能会降低吞吐量,但能减少饥饿现象。

示例代码:默认是非公平锁,如果需要公平锁,则在构造函数中传入 true

import java.util.concurrent.locks.ReentrantLock;// 默认非公平锁
ReentrantLock lock = new ReentrantLock();// 公平锁
ReentrantLock fairLock = new ReentrantLock(true);

3 可重入性

可重入意味着如果一个线程已经持有某个锁,那么它可以再次获取这个锁而不会被阻塞。这是非常有用的特性,因为它允许方法在调用其他可能也需要相同锁的方法时不会导致死锁。

示例代码:

public void reentrantExample() {lock.lock();try {// 第一次获取锁System.out.println("First time locked, thread: " + Thread.currentThread().getName());// 在已经持有锁的情况下再次获取锁lock.lock();try {System.out.println("Second time locked, thread: " + Thread.currentThread().getName());} finally {lock.unlock();  // 释放第二次获取的锁}} finally {lock.unlock();  // 释放第一次获取的锁}
}

输出结果:

First time locked, thread: main
Second time locked, thread: main

使用可重入锁的具体例子:假设有一个类,其中包含一个递归方法,且这个方法需要是线程安全的。由于方法会多次调用自身,因此需要一个可重入锁来确保同一个线程可以重复获取锁。

import java.util.concurrent.locks.ReentrantLock;public class FactorialCalculator {private final ReentrantLock lock = new ReentrantLock();public int factorial(int number) {lock.lock(); // 获取锁try {if (number <= 1) {return 1;} else {return number * factorial(number - 1); // 递归调用}} finally {lock.unlock(); // 释放锁}}
}

4 获取和释放锁

获取锁通过调用 lock() 方法,释放锁则通过 unlock() 方法。通常建议在 finally 块中释放锁,以确保即使发生异常也能正确释放锁。

public void doSomething() {lock.lock();try {// 执行同步代码} finally {lock.unlock();}
}

5 尝试获取锁

tryLock() 方法尝试获取锁,如果无法立即获得锁,则返回 false,不会阻塞线程。

if (lock.tryLock()) {  // 尝试获取锁,如果无法获取,则立即返回falsetry {// 执行同步代码} finally {lock.unlock();}
} else {// 锁未获得时的操作
}

tryLock() 方法有两个版本:

  • 无参数的 tryLock():尝试获取锁,如果锁可用则立即获取并返回 true,否则立即返回 false
  • 带超时的 tryLock(long timeout, TimeUnit unit):尝试获取锁,如果在指定的时间内可以获取到锁,则返回 true;如果超过了指定时间仍无法获取到锁,则返回 false

具体使用示例:

无参数的 tryLock():

import java.util.concurrent.locks.ReentrantLock;public class TryLockExample {private final ReentrantLock lock = new ReentrantLock();public void doWork() {if (lock.tryLock()) {  // 尝试获取锁try {// 执行需要同步的操作System.out.println("Thread " + Thread.currentThread().getName() + " is doing work.");} finally {lock.unlock();  // 确保释放锁}} else {// 如果无法获取锁,执行其他操作System.out.println("Thread " + Thread.currentThread().getName() + " could not get the lock and will do something else.");}}public static void main(String[] args) {TryLockExample example = new TryLockExample();// 创建两个线程来调用 doWork 方法Thread t1 = new Thread(() -> example.doWork(), "Thread-1");Thread t2 = new Thread(() -> example.doWork(), "Thread-2");t1.start();t2.start();}
}

在这个例子中,如果 Thread-1 先获取到了锁,那么 Thread-2 将无法获取锁,并且会直接输出“无法获取锁”的信息。

带超时的 tryLock(long timeout, TimeUnit unit) :

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;public class TryLockWithTimeoutExample {private final ReentrantLock lock = new ReentrantLock();public void doWork() {boolean locked = false;try {// 尝试在5秒内获取锁locked = lock.tryLock(5, TimeUnit.SECONDS);if (locked) {// 执行需要同步的操作System.out.println("Thread " + Thread.currentThread().getName() + " is doing work.");} else {// 如果在5秒内无法获取锁,执行其他操作System.out.println("Thread " + Thread.currentThread().getName() + " could not get the lock within 5 seconds and will do something else.");}} catch (InterruptedException e) {// 处理中断异常Thread.currentThread().interrupt();System.out.println("Thread " + Thread.currentThread().getName() + " was interrupted while waiting for the lock.");} finally {if (locked) {lock.unlock();  // 确保释放锁}}}public static void main(String[] args) {TryLockWithTimeoutExample example = new TryLockWithTimeoutExample();// 创建两个线程来调用 doWork 方法Thread t1 = new Thread(() -> example.doWork(), "Thread-1");Thread t2 = new Thread(() -> example.doWork(), "Thread-2");t1.start();t2.start();}
}

在这个例子中,如果 Thread-1 持有锁超过5秒钟,那么 Thread-2 将会在等待5秒后放弃尝试获取锁,并执行相应的逻辑。

使用场景

  • 避免死锁:当多个线程试图以不同的顺序获取多个锁时,可能会导致死锁。使用 tryLock() 可以帮助检测这种情况,并采取适当的措施。
  • 提高响应性:在某些情况下,你可能不希望线程一直等待锁,而是希望线程能够快速响应其他任务。这时可以使用 tryLock() 来检查锁是否可用,如果不可用就立即执行其他工作。
  • 资源竞争控制:在资源有限的情况下,可以使用 tryLock() 来尝试获取资源,如果获取不到则可以选择放弃或重试。

6 可中断的锁定

lockInterruptibly() 方法允许在等待获取锁的过程中响应中断。

try {lock.lockInterruptibly();  // 尝试获取锁,可以响应中断try {// 执行同步代码} finally {lock.unlock();}
} catch (InterruptedException e) {// 处理中断情况
}

示例代码:

import java.util.concurrent.locks.ReentrantLock;public class CancellableTask {private final ReentrantLock lock = new ReentrantLock();private boolean isCancelled = false;public void runTask() {Thread thread = new Thread(() -> {try {// 尝试获取锁,同时可以响应中断lock.lockInterruptibly();try {// 模拟任务执行while (!isCancelled) {System.out.println("Task is running...");// 假设任务需要一段时间完成Thread.sleep(1000);}} finally {lock.unlock();}} catch (InterruptedException e) {System.out.println("Task was interrupted, stopping execution.");// 通常在这里你会清理资源并退出// 重新设置中断状态,以便调用者知道该线程已被中断Thread.currentThread().interrupt();}});thread.start();// 在某个时刻决定取消任务try {Thread.sleep(3000); // 等待几秒钟} catch (InterruptedException e) {e.printStackTrace();}isCancelled = true;thread.interrupt(); // 中断线程}public static void main(String[] args) {CancellableTask task = new CancellableTask();task.runTask();}
}

当主线程决定取消任务时,它会中断工作线程。工作线程会在 lockInterruptibly() 调用处立即响应中断,并抛出 InterruptedException,从而允许任务快速终止。 

输出结果:

Task is running...
Task is running...
Task is running...
Task was interrupted, stopping execution.

除了使用 lockInterruptibly() 来实现中断锁,还可以使用带超时的 tryLock(long timeout, TimeUnit unit)来达成这一目的。以下是例子:

import java.util.concurrent.locks.ReentrantLock;public class ResourceService {private final ReentrantLock resourceLock = new ReentrantLock();public void useResource() {Thread thread = new Thread(() -> {try {if (resourceLock.tryLock()) {try {// 使用资源System.out.println("Using resource...");Thread.sleep(10000); // 模拟长时间操作} finally {resourceLock.unlock();}} else {System.out.println("Could not acquire the resource, operation is cancelled.");}} catch (InterruptedException e) {System.out.println("Resource usage was interrupted, releasing the resource.");resourceLock.unlock(); // 如果已经获得锁,则释放Thread.currentThread().interrupt(); // 保持中断状态}});thread.start();// 在某个时刻决定取消使用资源的操作try {Thread.sleep(5000); // 等待几秒钟} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt(); // 中断线程}public static void main(String[] args) {ResourceService service = new ResourceService();service.useResource();}
}

是的,tryLock(long time, TimeUnit unit) 方法也可以响应中断。当一个线程调用 tryLock(long time, TimeUnit unit) 时,它会尝试在指定的时间内获取锁。如果在这段时间内没有获取到锁,方法将返回 false。此外,如果在此期间线程被中断,该方法会立即抛出 InterruptedException 并且不会获取锁。 

注意:ReentrantLocklock() 方法确实不会响应中断。当一个线程调用 lock() 试图获取锁时,如果锁已经被其他线程持有,那么该线程将一直阻塞,直到它能够获取到锁为止。即使在此期间该线程被中断(例如通过调用 Thread.interrupt()),它也不会抛出 InterruptedException 或者以其他方式退出等待状态。只有在获取到锁之后,中断状态才会被检查。

7 条件变量

ReentrantLock 提供了条件变量 Condition,它可以替代传统的 Object.wait/notify 机制。这些对象类似于 Object 类中的 wait/notify 机制,但是更加灵活。每个 Condition 实例都可以独立地挂起和唤醒线程,这对于复杂的同步需求是非常有用的。

下面是一个具体的例子,展示了如何使用条件变量来实现生产者-消费者模式

在这个模式中,有一个共享缓冲区,生产者向缓冲区添加元素,消费者从缓冲区移除元素。当缓冲区满时,生产者必须等待;当缓冲区空时,消费者必须等待。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.LinkedList;public class ProducerConsumerExample {private final int BUFFER_SIZE = 5; // 缓冲区大小private final LinkedList<Integer> buffer = new LinkedList<>();private final ReentrantLock lock = new ReentrantLock();private final Condition notFull = lock.newCondition(); // 缓冲区不满的条件private final Condition notEmpty = lock.newCondition(); // 缓冲区不空的条件public void put(int value) throws InterruptedException {lock.lock();try {// 如果缓冲区已满,则等待while (buffer.size() == BUFFER_SIZE) {notFull.await();}// 向缓冲区添加元素buffer.add(value);System.out.println("Produced: " + value);// 唤醒可能正在等待的消费者notEmpty.signal();} finally {lock.unlock();}}public int get() throws InterruptedException {lock.lock();try {// 如果缓冲区为空,则等待while (buffer.isEmpty()) {notEmpty.await();}// 从缓冲区移除元素int value = buffer.removeFirst();System.out.println("Consumed: " + value);// 唤醒可能正在等待的生产者notFull.signal();return value;} finally {lock.unlock();}}public static void main(String[] args) {final ProducerConsumerExample example = new ProducerConsumerExample();Thread producerThread = new Thread(() -> {for (int i = 0; i < 20; i++) {try {example.put(i);Thread.sleep(100); // 模拟生产时间} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}});Thread consumerThread = new Thread(() -> {for (int i = 0; i < 20; i++) {try {example.get();Thread.sleep(200); // 模拟消费时间} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}});producerThread.start();consumerThread.start();try {producerThread.join();consumerThread.join();} catch (InterruptedException e) {e.printStackTrace();}}
}

代码解释: 

  • ReentrantLock 和 Condition:我们使用 ReentrantLock 来保护对共享资源(即缓冲区)的访问,并且创建了两个条件变量 notFull 和 notEmpty
  • put() 方法:生产者调用此方法向缓冲区添加数据。如果缓冲区已满,则生产者将调用 notFull.await() 等待直到有空间可用。一旦添加了数据,就通过 notEmpty.signal() 通知等待中的消费者。
  • get() 方法:消费者调用此方法从缓冲区获取数据。如果缓冲区为空,则消费者将调用 notEmpty.await() 等待直到有数据可用。一旦取出了数据,就通过 notFull.signal() 通知等待中的生产者。
  • 主线程:启动生产者和消费者线程,并等待它们完成。

8 性能

ReentrantLock 在某些情况下比 synchronized 更高效,尤其是在高度竞争的情况下。这是因为 ReentrantLock 可以使用自旋而不是完全阻塞来等待锁,这样可以减少上下文切换的成本。

9 使用场景

  • 当你需要比 synchronized 更细粒度的控制时,例如尝试获取锁、响应中断或使用多个条件变量。
  • 当你需要实现公平锁时。
  • 当你希望在性能上有所提升,并且能够处理高级并发模式时。

示例代码:

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();public void doSomething() {// 获取锁lock.lock();try {// 执行需要同步的操作System.out.println("Thread " + Thread.currentThread().getName() + " is doing something.");} finally {// 确保锁最终会被释放lock.unlock();}}public static void main(String[] args) {ReentrantLockExample example = new ReentrantLockExample();// 创建两个线程来调用 doSomething 方法Thread t1 = new Thread(() -> example.doSomething(), "Thread-1");Thread t2 = new Thread(() -> example.doSomething(), "Thread-2");t1.start();t2.start();}
}

在这个例子中,doSomething 方法使用 ReentrantLock 来确保每次只有一个线程可以执行其中的代码。注意在 finally 块中释放锁,以防止因异常导致锁未被释放的情ss

相关文章:

Java ReentrantLock

目录 1 互斥性 2 公平性 3 可重入性 4 获取和释放锁 5 尝试获取锁 6 可中断的锁定 7 条件变量 8 性能 9 使用场景 ReentrantLock 是 Java 提供的一种可重入的互斥锁&#xff0c;位于 java.util.concurrent.locks 包中&#xff0c;它实现了 Lock 接口。这个锁提供了与内…...

【Linux系统编程】第二十六弹---彻底掌握文件I/O:C/C++文件接口与Linux系统调用实践

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、回顾C语言文件接口 1.1、以写的方式打开文件 1.2、以追加的方式打开文件 2、初步理解文件 2.1、C文件接口 3、进一步理…...

数据分析-29-基于pandas的窗口操作和对JSON格式数据的处理

文章目录 1 窗口操作1.1 滑动窗口思想1.2 函数df.rolling2 JSON格式数据2.1 处理简单JSON对象和JSON列表2.1.1 处理简单的JSON结构2.1.2 处理空字段2.1.3 获取部分字段2.2 处理多级json2.2.1 展开所有级别(默认)2.2.2 自定义展开层级2.3 处理嵌套列表JSON3 参考附录1 窗口操作 …...

Ubuntu-WSL2一键设置代理操作

现状&#xff1a; Window11中拥有自己的代理软件 &#xff0c;可以科学上网已在WSL2中安装Ubuntu22.04 需求&#xff1a; Ubuntu-WSL2实现科学上网 实现&#xff1a; 参考&#xff1a;为 WSL2 一键设置代理 Linux 子系统中的网关指向的是 Windows&#xff0c;DNS 服务器指…...

ubuntu命令行连接wifi

在Ubuntu上&#xff0c;你可以通过命令行连接到Wi-Fi网络。以下是详细步骤&#xff0c;主要使用 nmcli 和 nmtui 命令。 方法 1&#xff1a;使用 nmcli nmcli 是 NetworkManager 的命令行界面&#xff0c;用于管理网络连接。以下是使用 nmcli 连接到 Wi-Fi 网络的步骤&#x…...

日常工作第10天:

vim 批量编辑的命令是 移动光标到列首。按键 CtrlV 进入 Visual block 模式&#xff0c;标记你想要进行编辑的列&#xff08;HJKL可以向左下上右移动光标&#xff09;。按键 ShiftI 进入 Insert 模式&#xff0c;在列首输入文本&#xff1b;或者 Shift A&#xff0c;追加文本…...

CNN+Transformer解说

CNN&#xff08;卷积神经网络&#xff09;和Transformer是两种在深度学习领域广泛使用的模型架构&#xff0c;它们在处理不同类型的数据和任务时各有优势。 CNN擅长捕捉局部特征和空间层次结构&#xff0c;而Transformer擅长处理序列数据和长距离依赖关系。 将CNN与Transform…...

jmeter中token测试

案例&#xff1a; 网站&#xff1a;http://shop.duoceshi.com 讲解&#xff1a;用三个接口来讲解 第一个接口code&#xff1a;GET http://manage.duoceshi.com/auth/code 第二个登录接口&#xff1a;http://manage.duoceshi.com/auth/login 第三个接口&#xff1a;http://…...

基于解压缩模块的JPEG同步重压缩检测论文学习

一、论文基本信息&#xff1a; 论文题目&#xff1a;基于解压缩模块的JPEG同步重压缩检测 作者&#xff1a;王金伟1 &#xff0c;胡冰涛1 &#xff0c;张家伟1 &#xff0c;马 宾2 &#xff0c;罗向阳3 &#xff08;1.南京信息工程大学计算机学院、网络空间安全学院&#xf…...

音视频入门基础:FLV专题(7)——Tag header简介

一、引言 从《音视频入门基础&#xff1a;FLV专题&#xff08;3&#xff09;——FLV header简介》中可以知道&#xff0c; 在FLV header之后&#xff0c;FLV文件剩下的部分应由PreviousTagSize和Tag组成。FLV文件 FLV header PreviousTagSize0 Tag1 PreviousTagSize1 Ta…...

【Linux 报错】“make: ‘xxxx‘ is up to date.” 解决办法

一、报错原因 我们使用 make 命令&#xff0c;想要将 text.c 文件编译形成 可执行文件 text 时&#xff0c;报错如下 make: test is up to date. 中文含义&#xff1a;test 文件已经达到最新状态 意思是&#xff1a; test.c 文件里面的 所有源代码都没有修改过&#xff0c;你…...

【FPGA开发】Xilinx FPGA差分输入时钟的使用方法

正文 以前在使用ZYNQ的领航者ZYNQ7020进行FPGA学习时&#xff0c;它们使用的单端50M的输入时钟&#xff0c;在verlog代码编写上比较简单&#xff0c;而现在使用Alinx的AXU3EG开发板时&#xff0c;发现它使用的是200M的差分输入时钟&#xff0c;哪这个时候&#xff0c;输入时钟要…...

面试扩展知识点

1.C语言中分为下面几个存储区 栈(stack): 由编译器自动分配释放堆(heap): 一般由程序员分配释放&#xff0c;若程序员不释放&#xff0c;程序结束时可能由OS回收全局区(静态区): 全局变量和静态变量的存储是放在一块的&#xff0c;初始化的全局变量和静态变量在一块区域&#…...

【经验分享】MyCAT 中间件

学习了一下数据量过大的解决方案,使用 MyCAT 中间件。 MyCAT 可以解决分布式事务、读写分离、主从、分片等一系列MySQL集群和分布式问题。 整体过程可以概括为拦截 - 分发 - 响应 例如设置 MyCAT 分片规则为每500万条数据就换一个数据库存储。 分库分表的中心思想都是将数据…...

Kotlin:1.8.0 的新特性

一、概述 Kotlin 1.8.0版本英语官方文档 Kotlin 1.8.0 中文官方文档 The Kotlin 1.8.0 release is out and here are some of its biggest highlights: Kotlin 1.8.0发布了&#xff0c;下面是它的一些亮点: JVM 平台新增实验性函数&#xff1a;递归复制或删除目录内容改进了 …...

深度学习之开发环境(CUDA、Conda、Pytorch)准备(4)

目录 1.CUDA 介绍 1.1 CUDA 的基本概念 1.2 CUDA 的工作原理 1.3 CUDA 的应用领域 2. 安装CUDA 2.1 查看GPU版本 2.2 升级驱动&#xff08;可选&#xff09; 2.3 查看CUDA版本驱动对应的支持的CUDA ToolKit工具包 2.4 下载Toolkit 2.5 安装&#xff08;省略&#xff0…...

10月2日笔记(内网资源探测篇)

内网资源探测 在内网渗透中&#xff0c;测试人员往往需要通过各种内网扫描技术来探测内网资源的情况&#xff0c;为后续的横向渗透做准备&#xff0c;通常需要发现内网存活的主机&#xff0c;并探测主机的操作系统、主机开放了哪些端口、端口上运行了哪些服务、服务的当前版本…...

SpringCloud-基于Docker和Docker-Compose的项目部署

一、初始化环境 1. 卸载旧版本 首先&#xff0c;卸载可能已存在的旧版本 Docker。如果您不确定是否安装过&#xff0c;可以直接执行以下命令&#xff1a; sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logro…...

Linux下的基本指令/命令(一)

目录 基本命令 1. Is命令/指令: 罗列当前目录下指定的文件或者目录. 2. pwd命令&#xff1a; 查看当前工作的路径 3. cd命令&#xff1a; 切换到指定路径下。 只能切换到目录中 4. tree命令: 树状显式目录 使用前要输入命令 yum install -y tree &#xff0c;用来安装一个…...

从零开始Ubuntu24.04上Docker构建自动化部署(四)Docker安装gitlab

你会发现所有gitlab都无法拉取到的情况下&#xff0c;经查找极狐下的gitlab还可以下载&#xff0c;具体如下&#xff1a; 建议保存地址&#xff1a; https://packages.gitlab.cn/#browse/searchkeyword%3Dgitlab 最新版本&#xff1a; sudo docker pull registry.gitlab.cn…...

Python爬虫实战:研究MechanicalSoup库相关技术

一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比

在机器学习的回归分析中&#xff0c;损失函数的选择对模型性能具有决定性影响。均方误差&#xff08;MSE&#xff09;作为经典的损失函数&#xff0c;在处理干净数据时表现优异&#xff0c;但在面对包含异常值的噪声数据时&#xff0c;其对大误差的二次惩罚机制往往导致模型参数…...

Go 并发编程基础:通道(Channel)的使用

在 Go 中&#xff0c;Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式&#xff0c;用于在多个 Goroutine 之间传递数据&#xff0c;从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...