【线程】Java多线程代码案例(2)
【线程】Java多线程代码案例(2)
- 一、定时器的实现
- 1.1Java标准库定时器
- 1.2 定时器的实现
- 二、线程池的实现
- 2.1 线程池
- 2.2 Java标准库中的线程池
- 2.3 线程池的实现
一、定时器的实现
1.1Java标准库定时器
import java.util.Timer;
import java.util.TimerTask;public class ThreadDemo5 {public static void main(String[] args) throws InterruptedException {Timer timer =new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1000");}},1000);System.out.println("hello main");}
}
1.2 定时器的实现
首先考虑,定时器中都需要都需要实现哪些元素呢?
- 需要有一个线程,负责掐时间
- 还需要有一个队列,能够保存所有添加进来的任务,这个队列要带有阻塞功能
因为这个任务,要先执行时间小的,再执行时间大的。此处我们可以实现一个优先级队列。那么时间小的任务就始终排在第一位,我们只需要关注队首元素是否到时间,如果队首没有到时间,那么后续其他元素,也一定没有到时间。
首先定义任务类,包含要执行的任务和时间
class MyTimerTask implements Comparable<MyTimerTask>{//执行时间private long time;//持有一个Runnableprivate Runnable runnable;public MyTimerTask(Runnable runnable,long delay){this.time=System.currentTimeMillis()+delay;this.runnable=runnable;}//实际要执行的任务public void run(){runnable.run();}public long getTime() {return time;}@Override//因为要加入优先级队列,必须能比较public int compareTo(MyTimerTask o) {return (int)(this.time-o.time);}
}
定义计时器
class MyTimer{//持有一个线程负责计时private Thread t=null;//优先级队列private PriorityQueue<MyTimerTask> queue =new PriorityQueue<>();//前面实现阻塞队列的逻辑,加锁private Object locker =new Object();//添加任务public void schedule(Runnable runnable,long delay){}//构造方法//注意执行任务并不需要我们写一个方法在main()函数中调用//这个是到时间自动执行的public MyTimer(){t=new Thread(()->{while(true){//到时间执行任务的逻辑}});}
}
那接下来我们就来分别实现这里的schedule
方法和构造函数中执行任务的逻辑:
schedule():
public void schedule(Runnable runnable,long delay){//入队列和出队列都需要打包成“原子性”的操作,加锁实现synchronized(locker){//新建任务MyTimerTask task=new MyTimerTask(runnable,delay);//加入队列queue.offer(task);//参考前面阻塞队列的实现,当队列为空时wait(),加入元素后notify()locker.notify();}
}
构造方法:
public MyTimer(){t=new Thread(()->{while(true){try{synchronized(locker){while(queue.isEmpty()){//阻塞直到加入新的任务后被notify()唤醒locker.wait();}//查看队首元素//peek不会将元素弹出MyTimerTask task=queue.peek;if(System.currentTimeMillis() >= task.getTime()){queue.poll();task.run();}else{//阻塞,释放锁(允许继续添加任务)//设置最大阻塞时间,阻塞到这个时间到了locker.wait(task.getTime()-System.currentTimeMillis());}}catch (InterruptedException e) {break;}}}); //启动线程t.start();
}
写到这里,就大功告成了,我们在main()函数中试验看一下运行结果:
public class ThreadDemo5{public static void main(String[] args) {MyTimer timer=new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println(3000);}},3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println(2000);}},2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println(1000);}},1000);Thread.sleep(4000);timer.cancel();}
}
这里我们再加一个方法,我们希望任务执行完成后,能够主动结束这个线程:
public void cancel(){t.interrupt();
}
这里需要考虑线程被提前唤醒抛出的异常,因此在构造方法中将捕获异常的操作改为break
;
计时器完整代码:
import java.util.PriorityQueue;class MyTimerTask implements Comparable<MyTimerTask>{//执行时间private long time;//持有一个Runnableprivate Runnable runnable;public MyTimerTask(Runnable runnable,long delay){this.time=System.currentTimeMillis()+delay;this.runnable=runnable;}//实际要执行的任务public void run(){runnable.run();}public long getTime() {return time;}@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time-o.time);}
}class MyTimer{//持有一个线程负责计时private Thread t=null;//任务队列——>优先级队列private PriorityQueue<MyTimerTask> queue =new PriorityQueue<>();//锁对象private Object locker=new Object();public void schedule(Runnable runnable,long delay){synchronized (locker) {//新建任务MyTimerTask task = new MyTimerTask(runnable, delay);//加入队列queue.offer(task);locker.notify();}}public void cancel(){t.interrupt();}public MyTimer(){t = new Thread(() -> {while (true) {try {synchronized (locker) {while (queue.isEmpty()) {//阻塞locker.wait();}//查看队首元素MyTimerTask task = queue.peek();if (System.currentTimeMillis() >= task.getTime()) {queue.poll();task.run();} else {//阻塞locker.wait(task.getTime()-System.currentTimeMillis());}}} catch (InterruptedException e) {break;}}});t.start();}
}
public class ThreadDemo5{public static void main(String[] args) throws InterruptedException {MyTimer timer=new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println(3000);}},3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println(2000);}},2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println(1000);}},1000);Thread.sleep(4000);timer.cancel();}
}
二、线程池的实现
2.1 线程池
最初我们提到线程这个概念,其实是一个“轻量级进程”。他的优势在于无需频繁地向系统申请/释放内存,提高了效率。但是随着线程的增多,频繁地创建/销毁线程也是一个很大的开销。解决方案有两种:
- 轻量级线程(协程),Java 21中引入了虚拟线程,就是这个东西。协程主要在Go语言中有较好的运用。
- 其次就是引入线程池的概念,无需频繁创建/销毁线程,而是一次性的创建好许多线程,每次直接取用,用完了放回线程池中。
为什么从线程池里取线程,会比从系统中申请更高效。
本质上在于去线程池里取线程,是一个用户态的操作,而向系统申请线程是一个内核态的操作。
还是以去银行取钱为例,向系统申请线程,就相当于找工作人员,在柜台取钱(工作人员收到请求后可能不会立即给你取钱),相对低效;而从线程池中取用线程,则相当于从ATM机里面取钱(从ATM机里面取钱是可以立即取到的),相对高效。
2.2 Java标准库中的线程池
这里我们可以细看一下这里的参数:
- corePoolSize(核心线程数)
一个线程池里,最少要有多少个线程,相当于正式工,不会被销毁。 - maximumPoolSize(最大线程数)
一个线程池里,最多要有多少个线程,相当于临时工,一段时间不干活就被销毁。 - keepAliveTime
临时工允许的空闲时间,超过这个时间,就被销毁。 - unit
keepAliveTime的时间单位 - BlockingQueue workQueue
传递任务的阻塞队列 - threadFactory
创建线程的工厂,参与具体的创建线程的工作。
这里涉及到工厂模式,试想这样的代码能否运行:
class Point{//笛卡尔坐标系public point(double x,double y){...}//极坐标系public point(double r,double a){...}
}
像这样的代码是无法运行的。因为他们具有相同的方法名和参数列表,无法完成重载。那如果确实想完成这样的操作,该怎么做呢?
class Point{public static Point makePointByXY(double x, double y){Point p=new Point();p.setX(x);p.setY(y);return p;}public static Point makePointByRA(double r,double a){Point p=new Point();p.setR(r);p.setA(a);return p;}
}
Point p=Point.makePointByXY(x,y);
Point p=Point.makePointByRA(r,a);
总的来说,通过静态方法封装new操作,在方法内部设定不同的属性完成对象的初始化,构造对象的过程,就是工厂模式。
- RejectedExecutionHandler handler
拒绝策略。如果这里的阻塞队列满了,此时要添加任务,就需要有一个应对策略。
策略 | 含义 | 备注 |
---|---|---|
AbortPolicy() | 超过负荷,抛出异常 | 所有任务都不做了 |
CallerRunsPolicy() | 调用者负责处理多出来的任务 | 所有任务都要做,新加的任务由添加任务的线程做 |
DiscardOldestPolicy() | 丢弃队列中最老的任务 | 不做最老的任务 |
DiscardPolicy() | 丢弃新来的任务 | 不做最新的任务 |
由于ThreadPoolExecutor本身用起来比较复杂,因此标准库还提供了一个版本,把ThreadPoolExecutor给封装了一下。Executors 工厂类,通过这个类来创建不同的线程池对象(内部把ThreadPoolExecutor创建好了并且设置了不同的参数)
大致有这么几种方法:
方法 | 用途 |
---|---|
newScheduleThreadExecutor() | 创建定时器线程,延时执行任务 |
newSingleThreadExecutor() | 只包含单个线程的线程池 |
newCachedThreadExecutor() | 线程数目能够动态扩容 |
newFixedThreadExecutor() | 线程数目固定 |
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadDemo6 {public static void main(String[] args) {ExecutorService service=Executors.newFixedThreadPool(4);service.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}});}
}
那么,对于一个多线程任务,创建多少个线程合适呢?
- 如果任务都是CPU密集型的(大部分时间在CPU上执行),此时线程数不应超过逻辑核心数;
- 如果任务都是IO密集型的(大部分时间在等待IO),此时线程数可以远远超过逻辑核心数;
- 由于实际的任务都是两种任务混合型的,一般通过实验的方式来得到最合适的线程数。
2.3 线程池的实现
我们可以实现一个简单的线程池(固定线程数目的线程池),要完成以下任务:
- 提供构造方法,指定创建多少个线程;
- 在构造方法中,创建线程;
- 有一个阻塞队列,能够执行要执行的任务;
- 提供submit()方法,添加新的任务
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;class MyThreadPoolExecutor{private List<Thread> threadList=new ArrayList<>();//阻塞队列private BlockingQueue<Runnable> queue=new ArrayBlockingQueue<>(10);public MyThreadPoolExecutor(int n){for(int i=0;i<n;i++){Thread t=new Thread(()-> {while (true) {try {//take操作也带有阻塞Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();threadList.add(t);}}public void submit(Runnable runnable) throws InterruptedException {//put操作带有阻塞功能queue.put(runnable);}
}
public class ThreadDemo6 {public static void main(String[] args) throws InterruptedException {MyThreadPoolExecutor executor=new MyThreadPoolExecutor(4);for(int i=0;i<1000;i++){int n=i;executor.submit(new Runnable() {@Overridepublic void run() {System.out.println("执行任务:"+n+",当前线程:"+Thread.currentThread().getName());}});}}
}
运行结果:
相关文章:
【线程】Java多线程代码案例(2)
【线程】Java多线程代码案例(2) 一、定时器的实现1.1Java标准库定时器1.2 定时器的实现 二、线程池的实现2.1 线程池2.2 Java标准库中的线程池2.3 线程池的实现 一、定时器的实现 1.1Java标准库定时器 import java.util.Timer; import java.util.Timer…...
IOU Loss详解
IoU(Intersection over Union是目标检测中常用的指标,用于评估预测框和真实框的重叠程度。基于 IoU 的损失函数(IoU Loss)是通过优化 IoU 值来提升模型预测框的精度。 IoU 的计算公式 给定预测框 ( B_p ) 和真实框 ( B_g )&#…...
nfs服务器
1、简介 NFS (Network File System,网络文件系统)是FreeBSD支持的文件系统中的一种,它允许网络中的计 算机(不同的计算机、不同的操作系统)之间通过TCP/IP网络共享资源,主要在unix系列操作系统上…...
Diving into the STM32 HAL----- IWDG and WWDG Timers笔记
墨菲定律指出,任何可能出错的事情都会出错。尤其是对于嵌入式系统来说,情况尤其如此。除了硬件故障也会对软件产生影响外,即使是最仔细的设计也可能会出现一些意外情况,导致我们设备的异常行为。这可能会产生重大成本,…...
使用statefulset管理pod
deployment主要管理无状态的pod,就算需要磁盘也是期望做动态扩容的。而对于那种有状态需要存储数据的,如log、数据库、消息中间件需要持久化数据的,则建议使用StatefulSet,我们的pod现在扩充了pv和pvc,那就使用Statefu…...
6个AI生成PPT的网站,pptai一键生成
演讲和展示PPT是当今办公和学习中的重要组成部分,无论是工作汇报、发表研究成果,还是课件培训,一款精心制作的PPT能够有效吸引观众的参与,并将你的信息深入人心。借助不断进步的人工智能技术,众多免费的AI PPT生成器可…...
【力扣】541.反转字符串2
问题描述 思路解析 每当字符达到2*k的时候,判断,同时若剩余字符>k,只对前k个进行判断(这是重点)因为字符串是不可变变量,所以将其转化为字符串数组,最后才将结果重新转变为字符串 字符串->字符数组 …...
如何优雅的用PyQt访问http(仅供参考)
使用pydantic或dataclaass创建一个数据存储对象 第一种 # coding: utf-8 from typing import Anyimport requests from pydantic import Field, BaseModelclass ResponseModel(BaseModel):status: bool Field(True, description"响应状态")message: str Field(请求…...
小程序租赁系统开发的优势与应用解析
内容概要 随着科技的迅猛发展,小程序租赁系统应运而生,成为许多企业优化业务的重要工具。首先,它提升了用户体验。想象一下,用户只需轻轻一点,就能够浏览和租赁心仪的商品,这种便捷的过程使繁琐的操作大大…...
Group Convolution(分组卷积)
分组卷积的优点: 减少参数数量: 在分组卷积中,每个分组只与一部分输入通道进行卷积运算,这意味着模型的参数数量会减少。例如,如果一个卷积层有8个输入通道和8个输出通道,且不使用分组,那么将有…...
uniapp在H5使用vue-router路由返回上一页不会触发销毁函数解决方法
问题:uniapp在H5使用vue-router路由,如果在H5平台上进行页面刷新操作,再返回上一页,可能会遇到beforeDestroy、destroyed、onUnload生命周期钩子不被触发的问题。这是因为在H5中,页面的刷新实际上是整个应用的重新加载…...
YOLOv8配置文件应该如何命名?
文末有重点 1.配置文件解析代码yolov8/ultralytics/nn/tasks.py 如下: def yaml_model_load(path):"""Load a YOLOv8 model from a YAML file."""import repath Path(path)if path.stem in (fyolov{d}{x}6 for x in nsmlx for d i…...
扫雷-完整源码(C语言实现)
云边有个稻草人-CSDN博客 在学完C语言函数之后,我们就有能力去实现简易版扫雷游戏了(成就感满满),下面是扫雷游戏的源码,快试一试效果如何吧! 在test.c里面进行扫雷游戏的测试,game.h和game.c…...
数据库(MySQL黑马)
基础篇 MySQL概述 数据库概述 数据库相关概念 主流的关系型数据库管理系统 MySQL数据库的安装与启动 下载:MySQL :: MySQL Community Downloads 安装步骤 MySQL―8.0.40超详细保姆级安装教程_mysql8.0.40安装教程-CSDN博客文章浏览阅读1k次。_mysql8.0.40安装教…...
UE5 Line Trace By Channel(通道线条追踪)节点
在 Unreal Engine 5 (UE5) 中,Line Trace By Channel 是一个常用于进行物理射线检测(raycasting)的节点。它会沿着一条从起点到终点的直线发射一条射线,并检测射线与世界中任何物体的碰撞。这个节点广泛应用于枪械射击、检测物体、…...
How to use the ‘git log‘ command to get change log for each file?
1. Using git log command #!/bin/bash# 目标文件名 output_file"git_log.csv"# 打印 CSV 文件的标题行 echo "CommitID,Author,Author Email,Date,Added-Lines Removed-Lines File" > $output_file echo "CommitID,Author,Author Email,Date,Add…...
node.js中实现router模块化管理
index.js const express require(express); const app express()// 导入路由模块 const router require(./router/index)// 注册路由模块 app.use(router)app.listen(3000, ()>{console.log(http://127.0.0.1:3000) })router/index.js const express require(express…...
突破性算法:让无人机集群在狭窄空间内穿针引线
导读 在建筑救援、森林搜索等任务中,无人机集群经常会遇到狭窄空间限制和动态障碍物变化等挑战。这些挑战会导致集群内部冲突,或在执行任务时因避让动态障碍物而导致系统混乱。实际应用场景和任务的严格特征往往使得全局搜索难以优化,而局部避…...
人工智能如何改变你的生活?
在我们所处的这个快节奏的世界里,科技融入日常生活已然成为司空见惯的事,并且切实成为了我们生活的一部分。在这场科技变革中,最具变革性的角色之一便是人工智能(AI)。从我们清晨醒来直至夜晚入睡,人工智能…...
C语言main()函数
C语言main()函数 argc(argument count) 在C或C程序中,argc(argument count的缩写)是一个由程序运行时环境自动提供的整数,用于指示传递给main函数的命令行参数的数量。因此,不需要(…...
【LC】162. 寻找峰值
题目描述: 峰值元素是指其值严格大于左右相邻值的元素。给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。 你可以假设 nums[-1] nums[n] -∞ 。 你必须…...
Zero to JupyterHub with Kubernetes上篇 - Kubernetes 离线二进制部署
前言: 纯个人记录使用。 搭建 Zero to JupyterHub with Kubernetes 上篇 - Kubernetes 离线二进制部署。搭建 Zero to JupyterHub with Kubernetes 中篇 - Kubernetes 常规使用记录。搭建 Zero to JupyterHub with Kubernetes 下篇 - Jupyterhub on k8s。 k8s二进…...
HTML5+JavaScript实现消消乐游戏
HTML5JavaScript实现消消乐游戏 点击两个相邻的方块来交换它们位置。 如果交换后形成三个或更多相同图案的方块连成一线,这些方块会被消除。 消除后,上方的方块会下落填补空缺,顶部会生成新的方块。 每消除一个方块得10分。例如࿰…...
深度学习:在PyTorch中进行模型验证完整流程
深度学习:在PyTorch中进行模型验证完整流程(以图像为例) 详细说明在PyTorch中进行模型验证的全过程。 模型验证的详细步骤和流程 1. 设置计算设备 选择合适的计算设备是性能优化的第一步。基于系统的资源(GPU的可用性…...
洛谷 P1747 好奇怪的游戏 C语言 bfs
题目: https://www.luogu.com.cn/problem/P1747#submit 题目描述 爱与愁大神坐在公交车上无聊,于是玩起了手机。一款奇怪的游戏进入了爱与愁大神的眼帘:***(游戏名被打上了马赛克)。这个游戏类似象棋,但…...
豆包MarsCode
01 AI代码陪练 来到豆包MarsCode官网:http://sqllb.com/6FRiH76 ,点击左上方的代码练习,就能看到一个AI代码陪练的练习场。 在左边栏,可以看到各种各样的算法题目,在右侧是MarsCode AI,中间是算法题目和…...
Docker网络模式:桥接(Bridge)模式与主机模式(Host)实操对比(一)
文章目录 前言一、桥接模式(Bridge Mode)前言 随着容器化技术的发展,Docker 已成为开发和部署应用的首选工具之一。Docker 不仅简化了应用的打包过程,还提供了多种网络模式来满足不同应用场景下的需求。本文将重点探讨两种常用的 Docker 网络模式——桥接模式(Bridge Mod…...
3. STM32_串口
数据通信的基础概念 什么是串行/并行通信: 串行通信就是数据逐位按顺序依次传输 并行通信就是数据各位通过多条线同时传输。 什么是单工/半双工/全双工通信: 单工通信:数据只能沿一个方向传输 半双工通信:数据可以沿两个方向…...
【Git】Git 命令参考手册
目录 Git 命令参考手册1. 创建仓库1.1 创建一个新的本地仓库1.2 克隆一个仓库1.3 克隆仓库到指定目录 2. 提交更改2.1 显示工作目录中已修改的文件,准备提交2.2 将文件添加到暂存区,准备提交2.3 将所有已修改的文件添加到暂存区,准备提交2.4 …...
EBS 中 Oracle Payables (AP) 模块的相关集成
Oracle E-Business Suite (EBS) 中的 Oracle Payables (AP) 模块是一个全面的应付账款管理系统,它不仅提供了丰富的功能来管理与供应商的财务交易,还通过与其他模块的紧密集成,实现了企业内部各个业务流程的无缝衔接。以下是 Oracle Payables…...
【LC】896. 单调数列
题目描述: 如果数组是单调递增或单调递减的,那么它是 单调 的。如果对于所有 i < j,nums[i] < nums[j],那么数组 nums 是单调递增的。 如果对于所有 i < j,nums[i]> nums[j],那么数组 nums 是…...
大语言模型LLM的微调代码详解
代码的摘要说明 一、整体功能概述 这段 Python 代码主要实现了基于 Hugging Face Transformers 库对预训练语言模型(具体为 TAIDE-LX-7B-Chat 模型)进行微调(Fine-tuning)的功能,使其能更好地应用于生成唐诗相关内容的…...
鸿蒙主流路由详解
鸿蒙主流路由详解 Navigation Navigation更适合于一次开发,多端部署,也是官方主流推荐的一种路由控制方式,但是,使用起来入侵耦合度高,所以,一般会使用HMRouter,这也是官方主流推荐的路由 Navigation官网地址 个人源码地址 路由跳转 第一步-定义路由栈 Provide(PageInfo) pag…...
GelSight和Meta AI合作推出多模态指尖形全向视触觉传感器Digit360
近日,触觉智能技术先锋 GelSight 与 Meta AI相关团队今宣布推出 Digit 360,这是一款呈人工指尖形状的触觉传感器,它能够以高精度将触摸数字化,从而提供较为丰富且详细的触觉数据。Digit 360将会拓展触觉传感研究领域,标…...
XX科技面试笔试题
笔试题 一、选择题 (每题5分,共20分) 1、构成存储器的最小单位是 ( ) A、bit B、Byte C、MB 2、下列不属于WEB服务的是 ( ) A、Apache B、Nginx C、IIS D、LVS 3、web服务器默认端口为 ( ) A、80 B、800 C、22 D、43 4、下列安装系统方法中,适合大量装机需求的是 ( ) A、U盘…...
AVL、B树和B+树
AVL树定义 AVL树(Adelson-Velsky 和 Landis 树)是一种自平衡的二叉搜索树(Binary Search Tree, BST),由苏联数学家Georgy Adelson-Velsky和Evgenii Landis在1962年提出。AVL树通过在每个节点上维护一个平衡因子&#…...
[SWPUCTF 2021 新生赛]include
参考博客: 文件包含 [SWPUCTF 2021 新生赛]include-CSDN博客 NSSCTF | [SWPUCTF 2021 新生赛]include-CSDN博客 考点:php伪协议和文件包含 PHP伪协议详解-CSDN博客 php://filter php://filter可以获取指定文件源码。当它与包含函数结合时,php://filter流会被当…...
ES----安装 elasticsearch入门,elasticsearch安装,centos安装es,centos安装elasticsearch
ES 如需要对应资源,请评论留言,或再最后视频中关注获取 1. 安装 1.1 安装es 创建网络(centos系统,docker环境) docker network create es-netdocker安装es —如果下载失败,请看我的docker配置镜像的文章…...
探索文件系统,Python os库是你的瑞士军刀
文章目录 探索文件系统,Python os库是你的瑞士军刀第一部分:背景介绍第二部分:os库是什么?第三部分:如何安装os库?第四部分:简单库函数使用方法1. 获取当前工作目录2. 改变当前工作目录3. 列出目…...
android studio引用so库
在工程中编译好的so库文件将在原始编译工程对应目录下:build/intermediates/cxx/Debug/xxxxxx/obj/ 其目录结构如上所示,包含生成的四个版本,每个文件夹下均包含c/c源码编译成的Android版本的libnavi.so库和提供应用接口的libnavi-lib.so库。…...
Ubuntu 服务器部署 Tomcat 并配置 SSL/TLS 证书
本文目录 准备登陆云服务器安装 Java下载 tomcat 包配置防火墙浏览器访问 Tomcat 默认页面以服务的形式运行 Tomcat创建 Tomcat 用户和组创建 systemd 服务文件启动 tomcat 服务 Tomcat webapps 文件目录部署一个静态网站tomcat 的配置文件 将域名解析到服务器Tomcat 配置 SSL/…...
不间断电源 (UPS) 对现代技术可靠性的影响
在这个技术型世界里,无论是在个人还是商业环境中,电力供应商提供的稳定供电都变得越来越重要。 不间断电源 (UPS) 系统是一种不可或缺的解决方案,可保证终端设备不受干扰地运行,在出现电源问题或故障时让用户继续工作。 这篇文章…...
Android 基础类(01)- Thread类 - readyToRun和threadLoop
一、前言: 在阅读AOSP代码过程中,我们经常会看到Thread子类重写两个方法:readyToRun和threadLoop,不清楚的同学,可能在这儿连调用逻辑都搞不清楚了,因为找不到谁调用了它。我这儿先不去深究Thread内部逻辑…...
【组件封装】uniapp vue3 封装一个自定义下拉刷新组件pullRefresh,带刷新时间和加载动画教程
文章目录 前言一、实现原理二、组件样式和功能设计三、scroll-view 自定义下拉刷新使用回顾相关属性:最终版完整代码: 前言 手把手教你封装一个移动端 自定义下拉刷新组件带更新时间和加载动画(PullRefresh),以uniapp …...
通过 JNI 实现 Java 与 Rust 的 Channel 消息传递
做纯粹的自己。“你要搞清楚自己人生的剧本——不是父母的续集,不是子女的前传,更不是朋友的外篇。对待生命你不妨再大胆一点,因为你好歹要失去它。如果这世上真有奇迹,那只是努力的另一个名字”。 一、crossbeam_channel 参考 cr…...
一起学习Fortran:如何安装Fortran
Fortran(全称Formula Translation,意为“公式翻译”)是一种通用编译命令式编程语言,适用于数值计算和科学计算。Fortran语言最初是由IBM在20世纪50年代为科学和工程应用程序而开发的,第一个Fortran版本——FORTRAN I在…...
社交新零售模式下“2+1 链动模式 S2B2C 商城小程序”的创新实践与发展策略
摘要:随着实体商业与社交网络深度融合,社交新零售蓬勃兴起,“21 链动模式 S2B2C 商城小程序”作为其中创新典范,融合独特激励机制与数字化运营优势,重塑零售生态。本文剖析该模式架构、运作逻辑,探讨其在私…...
【博主推荐】C# Winform 拼图小游戏源码详解(附源码)
文章目录 前言摘要1.设计来源拼图小游戏讲解1.1 拼图主界面设计1.2 一般难度拼图效果1.3 普通难度拼图效果1.4 困难难度拼图效果1.5 地域难度拼图效果1.6 内置五种拼图效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载结束语 前言 在数字浪潮汹涌澎湃的时代,程序开…...
贝叶斯统计的核心思想与基础知识:中英双语
中文版 贝叶斯统计的核心思想与基础知识 贝叶斯统计是以贝叶斯定理为核心,通过将先验知识和观测数据相结合,更新对参数或模型的认知的一种统计方法。它不仅强调概率的频率解释(频率统计学中概率描述事件的长期发生频率)…...
Verilog使用liberty文件中cell单元的demo
Liberty(.lib)文件是用来描述标准单元库中逻辑单元(如门电路、触发器等)的时序和功耗特性的,不是用来直接定义Verilog中的元件。在Verilog设计中,我们通常通过实例化模块(module)来创…...