详解Java多线程之循环栅栏技术CyclicBarrier
第1章:引言
大家好,我是小黑,工作中,咱们经常会遇到需要多个线程协同工作的情况。CyclicBarrier,直译过来就是“循环屏障”。它是Java中用于管理一组线程,并让它们在某个点上同步的工具。简单来说,咱们可以把一群线程想象成一队马拉雪橇的驯鹿,CyclicBarrier就像是一个指定的集合点,所有驯鹿必须到齐了,才能继续下一段旅程。
不过别担心,这听起来比实际复杂。实际上,CyclicBarrier提供了一种简单的方式来达到这个同步目的。它通过一个计数器来实现,这个计数器初始值是线程的数量。当一个线程到达屏障点时,计数器就减一。当计数器减到0时,表示所有线程都到齐了,然后咱们可以执行一些操作,或者继续执行下一步。
第2章:CyclicBarrier基础
要深入理解CyclicBarrier,咱们首先得知道它是怎么工作的。CyclicBarrier在Java的java.util.concurrent
包中,是并发编程的一部分。它主要用于让一组线程互相等待,直到所有线程都达到了一个公共屏障点(Barrier Point),然后这些线程才继续执行。
让小黑举个简单的例子。假设咱们有一个任务,需要四个线程同时开始执行。这就可以用CyclicBarrier来实现。小黑写了下面这段代码,来展示基本的用法:
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierExample {public static void main(String[] args) {// 创建一个新的CyclicBarrier,其中包括4个线程CyclicBarrier barrier = new CyclicBarrier(4, () -> System.out.println("所有线程到达屏障点,可以继续执行!"));// 创建四个线程for (int i = 0; i < 4; i++) {int threadNum = i;new Thread(() -> {try {System.out.println("线程 " + threadNum + " 正在执行任务");Thread.sleep(1000); // 模拟任务执行时间System.out.println("线程 " + threadNum + " 到达屏障点");barrier.await(); // 等待其他线程System.out.println("线程 " + threadNum + " 继续执行其他任务");} catch (Exception e) {e.printStackTrace();}}).start();}}
}
在这个例子中,咱们创建了一个CyclicBarrier
实例,这个实例要求四个线程都达到屏障点后才能继续执行。每个线程在执行自己的任务后,会调用barrier.await();
来等待其他线程。所有线程都调用了await()
方法后,计数器变为0,屏障就被克服了,每个线程继续执行它们之后的任务。
第3章:CyclicBarrier的核心特性
了解了CyclicBarrier的基本用法后,咱们来深入探讨一下它的核心特性。这些特性让CyclicBarrier成为并发编程中一个非常有用的工具,特别是在处理多线程同步问题时。
重用性
CyclicBarrier的一个显著特点是它的重用性。这意味着一旦所有等待线程都到达屏障,它就可以重置并重用。这个特性使得CyclicBarrier非常适合于那些需要多次等待一组线程到达同一点的情况。
让小黑用一个例子来说明这一点。假设咱们有一个处理数据的多阶段任务,每个阶段都需要所有线程完成后才能进入下一阶段。这里就可以运用CyclicBarrier的重用性。
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierReuseExample {private static final int THREAD_COUNT = 3;public static void main(String[] args) {CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT, () -> System.out.println("所有线程完成当前阶段,准备进入下一阶段!"));for (int i = 0; i < THREAD_COUNT; i++) {int threadNum = i;new Thread(() -> {try {for (int phase = 1; phase <= 3; phase++) { // 假设有三个阶段System.out.println("线程 " + threadNum + " 完成阶段 " + phase);barrier.await(); // 等待其他线程}} catch (Exception e) {e.printStackTrace();}}).start();}}
}
在这个例子中,每个线程都会经历三个阶段。每个阶段都使用相同的CyclicBarrier来同步线程。线程完成一个阶段后,就会等待其他线程。一旦所有线程都完成了该阶段,CyclicBarrier就会重置,让线程开始下一个阶段。
同步辅助功能
CyclicBarrier还提供了一个同步辅助功能:当所有线程都到达屏障时,可以执行一个预定义的动作。这是通过在CyclicBarrier的构造函数中提供一个Runnable来实现的。
这个功能非常有用,因为它允许咱们在所有线程都到达屏障后,执行一些处理,比如更新共享资源、合并结果等。小黑再来给大家展示一个例子:
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierActionExample {public static void main(String[] args) {CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("全部线程已到达屏障点,执行屏障动作"));for (int i = 0; i < 3; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + " 正在执行任务");Thread.sleep(2000); // 模拟任务执行时间barrier.await(); // 等待其他线程System.out.println(Thread.currentThread().getName() + " 继续执行后续任务");} catch (Exception e) {e.printStackTrace();}}).start();}}
}
在这个代码中,当所有线程都到达屏障点时,会执行一段指定的代码,即打印出“全部线程已到达屏障点,执行屏障动作”。这样的设计使得CyclicBarrier不仅仅是一个同步工具,还可以作为线程间协调的一种手段。
通过这些特性,CyclicBarrier成为了处理复杂同步问题的有力工具。它不仅能确保线程在继续执行前达到某个公共点,还能够在所有线程都准备好后执行。
第4章:CyclicBarrier的实际应用场景
并行计算
一个典型的应用场景是并行计算。假设咱们有一个大数据集,需要进行复杂的数据处理,这个处理过程可以分解为多个独立的子任务,每个子任务由一个单独的线程处理。但在进行下一步处理之前,必须确保所有子任务都完成了当前步骤。这里就是CyclicBarrier大显身手的时候。
来看看下面这个例子,小黑写了一段代码,模拟了这种情况:
import java.util.concurrent.CyclicBarrier;public class ParallelComputationExample {private static final int THREAD_COUNT = 4;public static void main(String[] args) {CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT, () -> System.out.println("所有子任务处理完成,准备进入下一步!"));for (int i = 0; i < THREAD_COUNT; i++) {new Thread(new Worker(i, barrier)).start();}}static class Worker implements Runnable {private final int threadNumber;private final CyclicBarrier barrier;Worker(int threadNumber, CyclicBarrier barrier) {this.threadNumber = threadNumber;this.barrier = barrier;}@Overridepublic void run() {try {System.out.println("线程 " + threadNumber + " 正在处理任务");Thread.sleep(2000); // 模拟任务处理时间System.out.println("线程 " + threadNumber + " 完成任务,等待其他线程");barrier.await(); // 等待其他线程完成} catch (Exception e) {e.printStackTrace();}}}
}
在这个例子中,咱们创建了四个线程,每个线程都代表一个数据处理任务。这些线程在完成自己的任务后,会等待其他线程完成,然后一起进入下一步。
第5章:深入CyclicBarrier的API
咱们已经看到了CyclicBarrier在实际场景中的一些应用,现在小黑要带大家更深入地了解一下CyclicBarrier的API。理解这些API对于充分利用CyclicBarrier的功能是至关重要的。
基本方法
CyclicBarrier提供了一些核心的方法来控制线程间的同步:
CyclicBarrier(int parties)
: 这是CyclicBarrier的构造函数,parties
指的是必须调用await
方法的线程数量。CyclicBarrier(int parties, Runnable barrierAction)
: 这个构造函数除了指定线程数外,还可以指定当所有线程都到达屏障时,要执行的操作。await()
: 线程调用这个方法告诉CyclicBarrier它已到达屏障点。如果所有线程都到达屏障,它们就会继续执行;否则,调用await
的线程会阻塞,等待其他线程。
示例:使用CyclicBarrier同步任务
为了更好地理解这些API,小黑准备了一个具体的例子。假设咱们有一个任务,需要多个线程协作完成,每个线程执行完各自的部分后,需要等待其他线程也执行完毕,然后统一进行下一步操作。
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierApiExample {public static void main(String[] args) {// 定义一个新的CyclicBarrier,需要3个线程协作CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有线程准备就绪,开始下一步操作"));for (int i = 0; i < 3; i++) {new Thread(new Task(barrier), "线程 " + i).start();}}static class Task implements Runnable {private final CyclicBarrier barrier;Task(CyclicBarrier barrier) {this.barrier = barrier;}@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " 正在执行任务");Thread.sleep(1000); // 模拟任务执行时间System.out.println(Thread.currentThread().getName() + " 完成任务,等待其他线程");barrier.await(); // 等待其他线程System.out.println(Thread.currentThread().getName() + " 开始执行后续操作");} catch (Exception e) {e.printStackTrace();}}}
}
在这个例子中,每个线程都在执行它的任务。完成任务后,它会等待其他线程也完成任务。只有当所有线程都执行了barrier.await()
方法之后,才会执行CyclicBarrier的barrierAction
,即打印出“所有线程准备就绪,开始下一步操作”。
异常处理
处理异常也是使用CyclicBarrier时需要考虑的一个重要方面。如果任何线程在等待过程中被中断或超时,或者屏障被重置,或者屏障的await
方法被中断,BrokenBarrierException
或InterruptedException
将会被抛出。这些异常需要被妥善处理,以确保程序的健壮性和正确性。
第6章:CyclicBarrier的高级用法
动态调整参与线程数
CyclicBarrier提供了一种机制,允许在运行时动态调整等待的线程数量。这在一些动态变化的并发场景中非常有用,比如线程数量会根据任务的不同而变化。
为了展示这个特性,小黑写了以下的例子。在这个例子中,咱们会创建一个CyclicBarrier,并在运行时根据需要动态调整它的屏障点:
import java.util.concurrent.CyclicBarrier;public class DynamicCyclicBarrierExample {public static void main(String[] args) {// 初始时,屏障点设置为3CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("屏障点动作执行"));for (int i = 0; i < 2; i++) { // 初始只启动两个线程new Thread(new Worker(barrier), "线程 " + i).start();}// 动态调整屏障点,现在需要4个线程到达屏障点barrier.reset(); // 重置CyclicBarrier,这也会打破任何当前等待的线程barrier = new CyclicBarrier(4, () -> System.out.println("新的屏障点动作执行"));for (int i = 0; i < 4; i++) { // 现在启动四个线程new Thread(new Worker(barrier), "线程 " + i).start();}}static class Worker implements Runnable {private final CyclicBarrier barrier;Worker(CyclicBarrier barrier) {this.barrier = barrier;}@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " 到达屏障点");barrier.await();System.out.println(Thread.currentThread().getName() + " 继续执行");} catch (Exception e) {e.printStackTrace();}}}
}
在这个例子中,咱们先创建了一个需要3个线程到达的CyclicBarrier。但后来因为需求变化,我们通过调用reset()
方法重置了CyclicBarrier,并创建了一个新的CyclicBarrier,这次需要4个线程。这展示了如何根据实际情况调整同步点的数量。
结合其他并发工具使用
CyclicBarrier还可以与Java的其他并发工具一起使用,以解决更复杂的并发问题。例如,可以将其与ExecutorService结合使用,以管理线程池中的一组任务。
看看下面的例子,小黑展示了如何将CyclicBarrier与线程池结合使用:
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CyclicBarrierWithExecutorServiceExample {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(4);CyclicBarrier barrier = new CyclicBarrier(4, () -> System.out.println("所有任务完成,准备下一轮执行"));for (int i = 0; i < 4; i++) {executorService.execute(new Worker(barrier));}executorService.shutdown();}static class Worker implements Runnable {private final CyclicBarrier barrier;Worker(CyclicBarrier barrier) {this.barrier = barrier;}@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " 正在执行任务");Thread.sleep(1000);barrier.await();System.out.println(Thread.currentThread().getName() + " 任务完成");} catch (Exception e) {e.printStackTrace();}}}
}
第7章:CyclicBarrier的问题和解决方案
1. BrokenBarrierException
的处理
当参与CyclicBarrier的某个线程在等待期间被中断,或者CyclicBarrier被重置,或者在屏障点等待的线程超时时,就会抛出BrokenBarrierException
异常。这通常意味着CyclicBarrier无法正常工作。
解决这个问题的关键是要正确处理这个异常。咱们可以设置适当的异常处理逻辑,确保即使在出现异常时,程序也能以一种预期的方式继续运行。例如,可以在捕获到BrokenBarrierException
时重置CyclicBarrier,或者采取其他恢复措施。
2. 超时的处理
如果咱们希望线程在等待达到屏障点的过程中不要无限期地等待,可以使用await(long timeout, TimeUnit unit)
方法,为等待设置一个超时时间。如果在指定的时间内没有所有的线程都到达屏障点,就会抛出TimeoutException
。
处理超时的策略可能包括重试机制或者回退逻辑。但重要的是要确保所有的线程在超时后都能正确地处理这种情况,避免资源泄漏或者线程阻塞。
示例代码:处理异常和超时
下面是一个示例,展示了如何在CyclicBarrier中处理BrokenBarrierException
和超时异常:
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;public class CyclicBarrierExceptionHandlingExample {public static void main(String[] args) {CyclicBarrier barrier = new CyclicBarrier(3);for (int i = 0; i < 3; i++) {new Thread(new Task(barrier), "线程 " + i).start();}}static class Task implements Runnable {private final CyclicBarrier barrier;Task(CyclicBarrier barrier) {this.barrier = barrier;}@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " 正在执行任务");Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + " 到达屏障点,等待其他线程");barrier.await(2, TimeUnit.SECONDS); // 设置超时时间为2秒} catch (InterruptedException e) {System.out.println(Thread.currentThread().getName() + " 被中断");} catch (BrokenBarrierException e) {System.out.println(Thread.currentThread().getName() + " 检测到屏障损坏");} catch (TimeoutException e) {System.out.println(Thread.currentThread().getName() + " 等待超时");}System.out.println(Thread.currentThread().getName() + " 继续执行后续操作");}}
}
在这个例子中,每个线程在执行任务后会尝试等待其他线程,但如果等待超过2秒,就会抛出TimeoutException
。同时,这个代码也演示了如何处理InterruptedException
和BrokenBarrierException
,确保线程在异常发生时能够正确地继续执行。
3. CyclicBarrier重置问题
在使用CyclicBarrier时,还可能遇到需要重置屏障的情况。这可以通过调用reset()
方法实现,但要注意这个操作会打破正在等待的线程。因此,在重置CyclicBarrier之前,需要确保所有线程都已经离开屏障点,或者咱们愿意接受打断它们的等待过程。
第8章:总结
- 基本用法:CyclicBarrier主要用于协调多个线程,确保它们在继续执行之前在某个公共点同步。
- 重用性:一个CyclicBarrier可以被重复使用,这对于那些分阶段执行的多线程任务非常有用。
- 异常处理:正确处理
BrokenBarrierException
和TimeoutException
对于构建健壮的并发应用至关重要。 - 与其他工具的结合:CyclicBarrier可以与Java的其他并发工具,如ExecutorService,配合使用,以处理更复杂的并发场景。
学习并发编程是一个持续的过程。技术总是在发展,新的挑战总是在出现。保持好奇心,不断学习,小黑相信你会在这条路上越走越远!
相关文章:
详解Java多线程之循环栅栏技术CyclicBarrier
第1章:引言 大家好,我是小黑,工作中,咱们经常会遇到需要多个线程协同工作的情况。CyclicBarrier,直译过来就是“循环屏障”。它是Java中用于管理一组线程,并让它们在某个点上同步的工具。简单来说…...
ebpf学习
学习ebpf相关知识 参考资料: awesome-ebpf 文章目录 初识准备ebpf.io介绍cilium的介绍内核文档Brendan Greggs Blog 的介绍书籍Learning eBPFWhat is eBPF? 交互式环境视频 基础知识学习学习环境搭建书籍阅读 项目落地流程整理环境搭建内核编译bcc环境变量zliblibelflibbpflib…...
【Linux】Linux系统编程——ls命令
【Linux】Linux 系统编程——ls 命令 1.命令概述 ls 命令是 Linux 和其他类 Unix 操作系统中最常用的命令之一。ls 命令是英文单词 list 的缩写,正如 list 的意思,ls 命令用于列出文件系统中的文件和目录。使用此命令,用户可以查看目录中的…...
QA面试题
1、质量保证(QA)是什么? QA代表质量保证。QA 是一组活动,旨在确保开发的软件满足 SRS 文档中提到的所有规范或要求。QA 遵循 PDCA 循环: 计划/Plan - 计划是质量保证的一个阶段,组织在此阶段确定构建高质量软件产品所需的过程。做…...
【国产mcu填坑篇】华大单片机(小华半导体)一、SPI的DMA应用(发送主机)HC32L136
最近需要用华大的hc32l136的硬件SPIDMA传输,瞎写很久没调好,看参考手册,瞎碰一天搞通了。。。 先说下我之前犯的错误,也是最宝贵的经验,供参考 没多看参考手册直接写(即使有点烂仍然提供了最高的参考价值。…...
【前后端的那些事】treeSelect树形结构数据展示
文章目录 tree-selector1. 新增表单组件2. 在父组件中引用3. 父组件添加新增按钮4. 树形组件4.1 前端代码4.2 后端代码 前言:最近写项目,发现了一些很有意思的功能,想写文章,录视频把这些内容记录下。但这些功能太零碎,…...
华为OD机试 - 最长子字符串的长度(二)(Java JS Python C)
题目描述 给你一个字符串 s,字符串 s 首尾相连成一个环形,请你在环中找出 l、o、x 字符都恰好出现了偶数次最长子字符串的长度。 输入描述 输入是一串小写的字母组成的字符串 输出描述 输出是一个整数 备注 1 ≤ s.length ≤ 5 * 10^5s 只包含小写英文字母用例 输入alolob…...
【VRTK】【Unity】【游戏开发】更多技巧
课程配套学习项目源码资源下载 https://download.csdn.net/download/weixin_41697242/88485426?spm=1001.2014.3001.5503 【概述】 本篇将较为零散但常用的VRTK开发技巧集合在一起,主要内容: 创建物理手震动反馈高亮互动对象【创建物理手】 非物理手状态下,你的手会直接…...
Spark 读excel报错,scala.MatchError
Spark3详细报错: scala.MatchError: Map(treatemptyvaluesasnulls -> true, location -> viewfs://path.xlsx, inferschema -> false, addcolorcolumns -> true, header -> true) (of class org.apache.spark.sql.catalyst.util.CaseInsensitiveMap)scala代码…...
【漏洞复现】Office365-Indexs-任意文件读取
漏洞描述 Office 365 Indexs接口存在一个任意文件读取漏洞,攻击者可以通过构造精心设计的请求,成功利用漏洞读取服务器上的任意文件,包括敏感系统文件和应用程序配置文件等。通过利用此漏洞,攻击者可能获得系统内的敏感信息,导致潜在的信息泄露风险 免责声明 技术文章…...
使用Python向RabbitMQ发送JSON数据只需要一个send_json方法
发送JSON数据 通过调用rabbitmq.send_json(channel, user, queueresult)能够更简单的实现发送JSON数据。 生产者 import json import rabbitmq# 建立连接 connection rabbitmq.get_connection()# 创建管道 channel connection.channel()# 创建队列 queue_name "user…...
Gitlab Gitee GitHub 远程仓库显示图片
大家好我是苏麟 , 今天出一期开发之外的如何在远程仓库展示图片 . 以GitLab为例 1.首先我们要把一张图片上传到远程仓库 . 2.第二步,点击图片 3.鼠标右键点击下载左键点击复制连接 4.找到我们要上传的md文件 , 把连接复制到 ( ) 里 , 这样上传md之后就可以看到图片了 . 这期就到…...
JS常用的几种事件
JavaScript常用的几种事件有: 点击事件:当用户点击某个元素时触发,常用于按钮、链接等交互元素。事件名称为"click"。 javascriptbutton.addEventListener(click, function() { alert(按钮被点击了!); }); 鼠标移动事…...
代码随想录算法训练营第一天| 27 移除元素 704 二分查找
目录 27 移除元素 704 二分查找 27 移除元素 快指针遍历,慢指针记录 class Solution { public:int removeElement(vector<int>& nums, int val) {int l 0,r 0;for(;r < nums.size();r){if(nums[r] val){}else{nums[l] nums[r];}}return l;} }; …...
深度生成模型(Deep Generative Models)
什么是机器学习 深度生成模型(Deep Generative Models)是一类利用深度学习方法生成新样本的模型。这些模型通常被用于生成与训练数据集相似的新数据,例如图像、文本或音频。深度生成模型的两个主要类型是生成对抗网络(GANs&#…...
C++(20):vector通过erase,erase_if删除符合条件的元素
C++20前,vector可以通过成员函数erase删除迭代器指定的元素,并返回被删除的下一个元素: iterator erase( iterator pos ); iterator erase( iterator first, iterator last ); 1.删除单个元素 #include <vector> #include <iostream> #include <algorithm&…...
树莓派ubuntu:新增用户
切换到Root用户 sudo -i创建新用户 useradd -m 新用户名设置密码 passwd 新用户名将新用户加入sudo用户组 adduser newname sudo拷贝数据 cp -R /home/旧用户名/* /home/新用户名/查看用户所属组 id 新用户名更改文件所属 sudo chown 新用户名:group /home/新用户名/*gr…...
C //练习 5-14 修改排序程序,使它能处理-r标记。该标记表明,以逆序(递减)方式排序。要保证-r和-n能够组合在一起使用。
C程序设计语言 (第二版) 练习 5-14 练习 5-14 修改排序程序,使它能处理-r标记。该标记表明,以逆序(递减)方式排序。要保证-r和-n能够组合在一起使用。 注意:代码在win32控制台运行,…...
CAN总线报文格式———标准数据帧
标准数据帧 : 用于节点向外传送数据 标准数据帧由帧起始、仲裁段、控制段、数据段、CRC段、ACK段、帧结束等组成。 一、总线空闲(Bus Idle) CAN总线空闲时,总线上会输出持续的高电平“1”。当总线空闲时任何连接的单元都可以开始发送新的报…...
DFT中的SCAN、BIST、ATPG基本概念
DFT中的SCAN、BIST、ATPG基本概念 SCAN 定义 扫描路径法是一种针对时序电路芯片的DFT方案,目标是在不影响正常功能的情况下来能够提高可控性和可观测性。 原理 原理是将时序电路可以模型化为一个组合电路网络和带触发器(Flip-Flop,简称FF)的时序电路…...
掌握 Vue 响应式系统,让数据驱动视图(下)
🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…...
apache、nginx、php 隐藏版本号
apache、nginx、php 隐藏版本号 针对的系统都是CentOS 1、没配置之前 1.1 Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.2.24 mod_wsgi/3.4 Python/2.7.5 1.2 Server: nginx/1.16.0 1.3 X-Powered-By:7.2.24 2、配置信息 不知道具体位置,可…...
sqoop的安装与使用
Sqoop是一个用于在hadoop与mysql之间传输数据的工具 Sqoop 环境搭建 (1)上传安装包:sqoop-1.4.6-cdh5.14.2.tar.gz到/opt/software (2)解压安装包:tar -zxf sqoop-1.4.6-cdh5.14.2.tar.gz -C /opt/install/ (3)创建软连接:ln -s /opt/install/sqoop-1.4.6-cdh5.14.2/ /opt/ins…...
【docker】Docker Stack 详细使用及注意事项
一、什么是 Docker Stack Docker Stack 是 Docker Swarm 环境中用于管理一组相关服务的工具。它使得在 Swarm 集群中部署、管理和扩展一组相互关联的服务变得简单。主要用于定义和编排容器化应用的多个服务。以下是 Docker Stack 的一些关键特点: 服务集合…...
Android开发基础(四)
Android开发基础(四) 本篇将从Android数据存储方式去理解Android开发。 Android数据存储方式 Android提供了多种数据存储方式。 一、SharedPreferences存储 主要用于存储一些简单的配置信息,如登录账号密码等; 这种存储方式采…...
HTML5+CSS3+JS小实例:音频可视化
实例:音频可视化 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><m…...
【写作】短篇《相遇与相守》
文章目录 前言背景角色故事梗概 第一章 缘分的邂逅第二章 心动的瞬间第三章 甜蜜的日子第四章 误会与和解第五章 共度风雨 前言 背景 时代背景 现代,一个充满忙碌和喧嚣的都市。这个都市是许多年轻人追求梦想和奋斗的地方,但也是许多人渴望寻找真挚感情…...
2024年最新软件测试面试题
Part1 1、你的测试职业发展是什么?【文末有面试文档免费领取】 测试经验越多,测试能力越高。所以我的职业发展是需要时间积累的,一步步向着高级测试工程师奔去。而且我也有初步的职业规划,前3年积累测试经验,按如何做…...
instanceof、对象类型转化、static关键字
instanceof 与 对象类型转换 instanceof是判断一个对象是否与一个类有关系的关键字 先看引用类型,再看实际类型 *例子:obj instanceof A 先看obj的类型是否与A有关联,无关联则报错,有关联则判断obj的实际类型 因为obj的实际类…...
学习笔记-python文件基本操作
1.文件的基本操作 open()打开函数 语法 : open(name,mode) name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)。 mode:设置打开文件的模式(访问模式):只读、写入、追加等。 # 打开文件open(): 如果报FileNotFoundError,文件路…...
珠海百度seo公司/织梦seo排名优化教程
环广西公路自行车世界巡回赛是经国际自行车联合会授权的世界顶级赛事,也是中国唯一的公路自行车世界巡回赛。此前,北海已经成功举办了两届环广西公路自行车世界巡回赛北海赛段比赛,今年是世巡赛环广西的第三年。自2017年环广西公路自行车世界…...
学设计的网站有哪些/华夏思源培训机构官网
Oracle软件在安装维护过程中长要和操作用户组(OS user group)打交道,从早前的只有oracle用户和dba组发展到今天11gr2中的grid用户和asm组,Oracle管理的日新月异可见一斑。我们在单实例(single-instance)环境中常用的三个操作用户组,分别是:oi…...
如何给自己公司做网站/黄页网络的推广网站有哪些类型
1.字符串中查找字符或字符串并返回索引,字符串中提取字符或字符串 函数find_first_of() 查找在字符串中第1个出现的字符c,而函数find_last_of()查找最后一个出现的c。匹配的位置是返回值。如果没有匹配发生,则函数返回-1。 int find_first_…...
网站交互方式/网站推广论坛
Hankson的趣味题 题意: 就是给你多组测试样例,每次给你a0,a1,b0,b1,让你找出有多少不同的x,满足gcd(a0,x)a1并且lcm(b0,x)b1。T 2000,1≤a0,a1,b0,b1≤2∗1e9。 思考: 很久以前刷acwing的时候,没把这题…...
昆明利于优化的网站/南宁seo外包服务商
概述今天开发反馈同样的sql且数据量一致,但在综测环境需要140多秒,而在开发环境只需要1秒多,这是什么原因呢?下面一起来看看吧~问题sqlSELECT ty.IS_FIXATION AS isFixation,CASE WHEN ty.INSPECTION_RULES IS NOT NULL AND ty.I…...
统一企业官方网站/奶茶的营销推广软文
一、概要 在JAVA应用程序特别是基于WEB的程序中,经常遇到字符的编码问题。为了防止出现乱码,首先需要了解JAVA是如何处理字符的,这样就可以有目的地在输入/输出环节中增加必要的转码。其次,由于各种服务器有不同的处理方式&#x…...