Java高并发编程基础之Thread构造函数大有内涵
引言
在Java中,Thread类提供了许多丰富的构造函数,以便于创建和管理线程。使得可以根据具体需求来创建和配置线程对象,从而实现更灵活、可扩展的多线程编程。
- Thread类的无参构造函数可以创建一个新的线程对象,然后通过调用start()方法来启动线程的执行。
- Thread类提供了其他一些常用的构造函数。例如,可以使用带有Runnable参数的构造函数来创建一个线程对象,并传入一个实现了Runnable接口的对象,从而指定线程要执行的任务。这种方式更常用,因为它可以使得代码更具可重用性和灵活性。
- Thread类提供了带有线程名称、线程优先级等参数的构造函数,可以通过这些构造函数来设置线程的属性。
- 使用带有ThreadGroup参数的构造函数将线程添加到特定的线程组中。线程组可以方便地对一组线程进行管理和控制。
Thread的构造函数
public Thread() {this(null, null, "Thread-" + nextThreadNum(), 0);}public Thread(Runnable target) {this(null, target, "Thread-" + nextThreadNum(), 0);}Thread(Runnable target, @SuppressWarnings("removal") AccessControlContext acc) {this(null, target, "Thread-" + nextThreadNum(), 0, acc, false);}public Thread(ThreadGroup group, Runnable target) {this(group, target, "Thread-" + nextThreadNum(), 0);}public Thread(String name) {this(null, null, name, 0);}public Thread(ThreadGroup group, String name) {this(group, null, name, 0);}public Thread(Runnable target, String name) {this(null, target, name, 0);}public Thread(ThreadGroup group, Runnable target, String name) {this(group, target, name, 0);}public Thread(ThreadGroup group, Runnable target, String name,long stackSize) {this(group, target, name, stackSize, null, true);}public Thread(ThreadGroup group, Runnable target, String name,long stackSize, boolean inheritThreadLocals) {this(group, target, name, stackSize, null, inheritThreadLocals);}
线程的命名
在构造线程的时候可以为线程起一个有特殊意义的名字,这也是比较好的一种做法。尤其在一个线程比较多的程序中,为线程赋予一个包含特殊意义的名字有助于问题的排查和线程的跟踪,强烈推荐在构造线程的时候赋予它一个名字。
线程的默认命名
使用Thread有的构造函数没有提供名称的参数,这个时候系统会生成一个默认的线程名称。默认的名称都是 Thread-加上线程编码数字
。以下是比较常用的不带线程名称的构造器函数。
/*** Allocates a new {@code Thread} object. This constructor has the same* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}* {@code (null, null, gname)}, where {@code gname} is a newly generated* name. Automatically generated names are of the form* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.*/public Thread() {this(null, null, "Thread-" + nextThreadNum(), 0);}/*** Allocates a new {@code Thread} object. This constructor has the same* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}* {@code (null, target, gname)}, where {@code gname} is a newly generated* name. Automatically generated names are of the form* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.** @param target* the object whose {@code run} method is invoked when this thread* is started. If {@code null}, this classes {@code run} method does* nothing.*/public Thread(Runnable target) {this(null, target, "Thread-" + nextThreadNum(), 0);}/*** Allocates a new {@code Thread} object. This constructor has the same* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}* {@code (group, target, gname)} ,where {@code gname} is a newly generated* name. Automatically generated names are of the form* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.** @param group* the thread group. If {@code null} and there is a security* manager, the group is determined by {@linkplain* SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.* If there is not a security manager or {@code* SecurityManager.getThreadGroup()} returns {@code null}, the group* is set to the current thread's thread group.** @param target* the object whose {@code run} method is invoked when this thread* is started. If {@code null}, this thread's run method is invoked.** @throws SecurityException* if the current thread cannot create a thread in the specified* thread group*/public Thread(ThreadGroup group, Runnable target) {this(group, target, "Thread-" + nextThreadNum(), 0);}
命名线程
构造函数提供了name
作为线程名称的参数用于初始化构造函数,开发过程提供名称更利于问题的排查。
/*** Allocates a new {@code Thread} object. This constructor has the same* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}* {@code (null, null, name)}.** @param name* the name of the new thread*/public Thread(String name) {this(null, null, name, 0);}
/*** Allocates a new {@code Thread} object. This constructor has the same* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}* {@code (group, null, name)}.** @param group* the thread group. If {@code null} and there is a security* manager, the group is determined by {@linkplain* SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.* If there is not a security manager or {@code* SecurityManager.getThreadGroup()} returns {@code null}, the group* is set to the current thread's thread group.** @param name* the name of the new thread** @throws SecurityException* if the current thread cannot create a thread in the specified* thread group*/public Thread(ThreadGroup group, String name) {this(group, null, name, 0);}/*** Allocates a new {@code Thread} object. This constructor has the same* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}* {@code (null, target, name)}.** @param target* the object whose {@code run} method is invoked when this thread* is started. If {@code null}, this thread's run method is invoked.** @param name* the name of the new thread*/public Thread(Runnable target, String name) {this(null, target, name, 0);}/**** @param group* the thread group. If {@code null} and there is a security* manager, the group is determined by {@linkplain* SecurityManager#getThreadGroup SecurityManager.getThreadGroup()}.* If there is not a security manager or {@code* SecurityManager.getThreadGroup()} returns {@code null}, the group* is set to the current thread's thread group.** @param target* the object whose {@code run} method is invoked when this thread* is started. If {@code null}, this thread's run method is invoked.** @param name* the name of the new thread** @throws SecurityException* if the current thread cannot create a thread in the specified* thread group or cannot override the context class loader methods.*/public Thread(ThreadGroup group, Runnable target, String name) {this(group, target, name, 0);}
修改线程的名字
不论使用的是默认的函数命名规则,还是指定了一个特殊的名字,在线程启动之前还有一个机会可以对其进行修改,一旦线程启动,名字将不再被修改,下面是 Thread
的 setName
源码:
/*** Changes the name of this thread to be equal to the argument {@code name}.* <p>* First the {@code checkAccess} method of this thread is called* with no arguments. This may result in throwing a* {@code SecurityException}.** @param name the new name for this thread.* @throws SecurityException if the current thread cannot modify this* thread.* @see #getName* @see #checkAccess()*/public final synchronized void setName(String name) {checkAccess();if (name == null) {throw new NullPointerException("name cannot be null");}this.name = name;if (threadStatus != 0) {setNativeName(name);}}
线程的父子关系
Thread的所有构造函数,最终都会去调用一个内部方法init(JDK8)或者同一个构造函数java.lang.Thread#Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean)
(JDK17)用于线程的真实创建,通过源码发现新创建的任何一个线程都会有一个父线程。
以JDK17创建线程的源码为例:
Thread parent = currentThread(); // 调用获取当前线程函数
this.daemon = parent.isDaemon();// 通过父线程守护参数设置当前线程守护线程参数
this.priority = parent.getPriority();// 通过父线程参数设置当前线程优先级参数
完整源码如下:
/*** Initializes a Thread.** @param g the Thread group* @param target the object whose run() method gets called* @param name the name of the new Thread* @param stackSize the desired stack size for the new thread, or* zero to indicate that this parameter is to be ignored.* @param acc the AccessControlContext to inherit, or* AccessController.getContext() if null* @param inheritThreadLocals if {@code true}, inherit initial values for* inheritable thread-locals from the constructing thread*/@SuppressWarnings("removal")private Thread(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {if (name == null) {throw new NullPointerException("name cannot be null");}this.name = name;Thread parent = currentThread(); // 调用获取当前线程函数SecurityManager security = System.getSecurityManager();if (g == null) {/* Determine if it's an applet or not *//* If there is a security manager, ask the security managerwhat to do. */if (security != null) {g = security.getThreadGroup();}/* If the security manager doesn't have a strong opinionon the matter, use the parent thread group. */if (g == null) {g = parent.getThreadGroup();}}/* checkAccess regardless of whether or not threadgroup isexplicitly passed in. */g.checkAccess();/** Do we have the required permissions?*/if (security != null) {if (isCCLOverridden(getClass())) {security.checkPermission(SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);}}g.addUnstarted();this.group = g;this.daemon = parent.isDaemon();// 通过父线程守护参数设置当前线程守护线程参数this.priority = parent.getPriority();// 通过父线程参数设置当前线程优先级参数if (security == null || isCCLOverridden(parent.getClass()))this.contextClassLoader = parent.getContextClassLoader();elsethis.contextClassLoader = parent.contextClassLoader;this.inheritedAccessControlContext =acc != null ? acc : AccessController.getContext();this.target = target;setPriority(priority);if (inheritThreadLocals && parent.inheritableThreadLocals != null)this.inheritableThreadLocals =ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);/* Stash the specified stack size in case the VM cares */this.stackSize = stackSize;/* Set thread ID */this.tid = nextThreadID();}
currentThread()
是获取当前线程,在线程生命周期中,线程的最初状态为NEW
,没有执行start
方法之前,它只能算是一个Thread
的实例,并不意味着一个新的线程被创建,因此currentThread
代表的将会是创建它的那个线程,可以得出以下结论:
- 一个线程的创建肯定是由另一个线程完成的。
- 被创建线程的父线程是创建它的线程。
main
函数所在的线程是由JVM
创建的,也就是main
线程,那就意味着前面示例代码中创建的所有线程,其父线程都是 main
线程。
Thread 与 ThreadGroup
在初始化线程的构造函数有如下这段代码:
Thread parent = currentThread(); // 调用获取当前线程函数
SecurityManager security = System.getSecurityManager();
if (g == null) {/* Determine if it's an applet or not *//* If there is a security manager, ask the security managerwhat to do. */if (security != null) {g = security.getThreadGroup();}/* If the security manager doesn't have a strong opinionon the matter, use the parent thread group. */if (g == null) {g = parent.getThreadGroup();}
}/* checkAccess regardless of whether or not threadgroup isexplicitly passed in. */
g.checkAccess();/** Do we have the required permissions?*/
if (security != null) {if (isCCLOverridden(getClass())) {security.checkPermission(SecurityConstants.SUBCLASS_IMPLEMENTATION_PERMISSION);}
}g.addUnstarted();this.group = g;
通过对源码分析,在创建线程时没有显示给出 ThreadGroup g
,也就是 g==null
这个判断为true,构造函数会隐式的给当前线程实例添加一个默认的ThreadGroup
。这个ThreadGroup
就是当前运行线程(或者说是父线程)的ThreadGroup
。
通过代码测试如下:
package engineer.concurrent.battle.onebasic;/*** 测试 Thread 与 ThreadGroup*/
public class ThreadGroupTest {public static void main(String[] args) {Thread t1 = new Thread("t1");ThreadGroup group = new ThreadGroup("group1");Thread t2 = new Thread(group,"t2");ThreadGroup mainThreadGroup = Thread.currentThread().getThreadGroup();System.out.println("Main thread belong group:"+ mainThreadGroup.getName());System.out.println("tl and main belong the same group:" + (mainThreadGroup== t1.getThreadGroup()));System.out.println("t2 thread group not belong main group:" + (mainThreadGroup== t2.getThreadGroup()));System.out.println("t2 thread group belong main TestGroup:" + (group == t2.getThreadGroup()));}
}
输出结果如下:
Main thread belong group:main
tl and main belong the same group:true
t2 thread group not belong main group:false
t2 thread group belong main TestGroup:true
Thread与stack size
在Thread的构造函数中有一个stackSize
参数,说明内容为the desired stack size for the new thread, or zero to indicate that this parameter is to be ignored.
。翻译过来的意思是stackSize
指的是新线程栈的大小,可以传0代表忽略这个参数而使用默认的栈内存大小。栈内存大小这块内容是与JVM配置栈大小相关的。下面基于此分析。
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html 中提到了stackSize
。具体内容如下:
The stack size is the approximate number of bytes of address space that the virtual machine is to allocate for this thread's stack. The effect of the stackSize parameter, if any, is highly platform dependent.On some platforms, specifying a higher value for the stackSize parameter may allow a thread to achieve greater recursion depth before throwing a StackOverflowError. Similarly, specifying a lower value may allow a greater number of threads to exist concurrently without throwing an OutOfMemoryError (or other internal error). The details of the relationship between the value of the stackSize parameter and the maximum recursion depth and concurrency level are platform-dependent. On some platforms, the value of the stackSize parameter may have no effect whatsoever.The virtual machine is free to treat the stackSize parameter as a suggestion. If the specified value is unreasonably low for the platform, the virtual machine may instead use some platform-specific minimum value; if the specified value is unreasonably high, the virtual machine may instead use some platform-specific maximum. Likewise, the virtual machine is free to round the specified value up or down as it sees fit (or to ignore it completely).Specifying a value of zero for the stackSize parameter will cause this constructor to behave exactly like the Thread(ThreadGroup, Runnable, String) constructor.Due to the platform-dependent nature of the behavior of this constructor, extreme care should be exercised in its use. The thread stack size necessary to perform a given computation will likely vary from one JRE implementation to another. In light of this variation, careful tuning of the stack size parameter may be required, and the tuning may need to be repeated for each JRE implementation on which an application is to run.Implementation note: Java platform implementers are encouraged to document their implementation's behavior with respect to the stackSize parameter.堆栈大小是虚拟机为该线程的堆栈分配的大致字节数。stackSize参数的效果在很大程度上取决于平台。在某些平台上,为stackSize参数指定较高的值可能允许线程在引发StackOverflowError之前实现更大的递归深度。同样,指定较低的值可能允许更多的线程并发存在,而不会引发OutOfMemoryError(或其他内部错误)。stackSize参数的值与最大递归深度和并发级别之间的关系细节因平台而异。在某些平台上,stackSize参数的值可能根本不起作用。虚拟机可以将stackSize参数视为建议。如果指定的值对于平台来说过低,虚拟机可能会使用一些特定于平台的最小值;如果指定的值过高,虚拟机可能会使用一些特定于平台的最大值。同样地,虚拟机可以自行决定是否将指定的值向上舍入或向下舍入(或完全忽略它)。对于stackSize参数设置为0,该构造函数的行为将与Thread(ThreadGroup, Runnable, String)构造函数完全相同。由于此构造函数的行为依赖于平台,使用时应极度谨慎。执行给定计算所需的线程堆栈大小可能因JRE实现而异。鉴于这种差异,可能需要对stackSize参数进行仔细调整,并且在应用程序要运行的每个JRE实现上可能需要重复调整。实施注意事项:鼓励Java平台实现者记录其实现与stackSize参数相关的行为。
通过翻译总结有如下几个要点:
- 堆栈大小是虚拟机为该线程的堆栈分配的大致字节数。
- stackSize参数的效果在很大程度上取决于平台。
- 执行给定计算所需的线程堆栈大小可能因JRE实现而异。
- 在应用程序要运行的每个JRE实现上可能需要重复调整。
一般情况下stackSize参数指定较高的值可能允许线程在引发StackOverflowError之前实现更大的递归深度,指定较低的值可能允许更多的线程并发存在,而不会引发OutOfMemoryError(或其他内部错误)。
package engineer.concurrent.battle.onebasic;
import org.junit.jupiter.api.Test;public class ThreadStackSizeTest {private final static int THREAD_COUNT = Integer.MAX_VALUE;@Testpublic void testStackSize1() {// 6914stackSizeRunMethod(10);}@Testpublic void testStackSize2() {// 4009stackSizeRunMethod(100);}@Testpublic void testStackSize3() {//6461stackSizeRunMethod(1000);}@Testpublic void testStackSize4() {//153stackSizeRunMethod(10000);}public static void stackSizeRunMethod(int stackSize) {ThreadGroup group = new ThreadGroup("stackSizeRunMethod");Runnable runnable = new Runnable() {public void run() {int i = 1;try {recurse(i);} catch (Exception e) {System.out.println(e.getMessage());} catch (Error e) {System.out.println(e.getMessage());}}private void recurse(int i) {System.out.println(i);if (i < THREAD_COUNT) {recurse(i + 1);}}};Thread thread = new Thread(group, runnable, "ThreadStackSizeTest", stackSize);thread.start();}
}
经过测试笔者选择的Oracle OpenJDK 17,stackSize与递归深度关系不是很大,有的时候甚至成反比。
守护线程
Java中通过将线程设置为守护线程(daemon thread)来指示该线程为守护线程。守护线程在没有用户线程(也就是剩余线程均为守护线程,JVM会退出)继续运行时会自动终止。这对于执行后台任务或提供服务的线程非常有用,因为它们可以在不再需要时自动关闭。
要将线程设置为守护线程,可以使用setDaemon(true)
方法。这应该在启动线程之前调用。
守护线程应该谨慎使用,因为它们可能会在程序退出时突然终止,这可能导致一些任务未能完成。
以下是一个简单的示例:
package engineer.concurrent.battle.onebasic;public class DaemonThread {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {while (true) {System.out.println("I am a daemon thread");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});// 将线程设置为守护线程thread.setDaemon(true);thread.start();Thread.sleep(2000);System.out.println("I am main thread and end");}
}
上面例子中main主线程运行结束则守护线程thread线程也结束运行。
参考
- 《Java高并发编程详解:多线程与架构设计》
- Java Thread Doc
关于作者
来自一线全栈程序员nine的八年探索与实践,持续迭代中。欢迎关注“雨林寻北”或添加个人卫星codetrend(备注技术)。
相关文章:
Java高并发编程基础之Thread构造函数大有内涵
引言 在Java中,Thread类提供了许多丰富的构造函数,以便于创建和管理线程。使得可以根据具体需求来创建和配置线程对象,从而实现更灵活、可扩展的多线程编程。 Thread类的无参构造函数可以创建一个新的线程对象,然后通过调用star…...

2023年12月 Python(六级)真题解析#中国电子学会#全国青少年软件编程等级考试
Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,共50分) 第1题 运行以下程序,输出的结果是?( ) class A():def __init__(self,x):self.x=x...

代码随想录算法训练营第一天
● 今日学习的文章链接和视频链接 ● 自己看到题目的第一想法 1. 704二分法: 方法一: 整个数组是 左闭右闭区间 [ ] left指针指向数组开始下标, right 指针指向数组最后下表nums.size()-1, mid为 (leftright) /2循环条件 left<rightnu…...

基于 java springboot+layui仓库管理系统
基于 java springbootlayui仓库管理系统设计和实现 博主介绍:5年java开发经验,专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文末获取源…...

电商平台商家结算
本文主要分析了目前电商清结算的流程以及自己对电商清结算的看法。 基本概念 先说下电商平台清结算的概念。简单说就是收的用户(C端)的付款,经过清分,再结算给对应商家。当然,这里排除那种资金不通过第三方,…...
AIGC 实战:如何使用 Docker 在 Ollama 上离线运行大模型(LLM)
Ollama简介 Ollama 是一个开源平台,用于管理和运行各种大型语言模型 (LLM),例如 Llama 2、Mistral 和 Tinyllama。它提供命令行界面 (CLI) 用于安装、模型管理和交互。您可以使用 Ollama 根据您的需求下载、加载和运行不同的 LLM 模型。 Docker简介 D…...
MII、RMII、GMII和RGMII,以太网接口中常见的几种标准接口
MII、RMII、GMII和RGMII是以太网接口中常见的几种标准接口,它们在硬件设计中有各自的特点和注意事项。 MII(Media Independent Interface):MII是一种传统的以太网物理层接口标准,它包括4位数据总线、时钟和控制信号。…...

SpringCloudConfig+SpringCloudBus+Actuator+Git实现Eureka关键配置属性热更新(全程不重启服务)
文章目录 前言1.痛点2.解决方案3.具体实现3.1搭建热配置服务3.2编写配置文件3.3搭建版本控制仓库3.4Eureka-Client引入以下依赖3.5Eureka-Client微服务编写以下配置bootstrap.yml提前加载3.6分别编写测试Controller3.7测试效果3.8下线场景压测 4.SpringCloudBus优化5.写到最后 …...
郑州大学2024年寒假训练 Day7:数论
感觉这一块讲的有点太少了,只有辗转相除法,拓展欧几里得定理,素数筛,快速幂和逆元五个内容。数论的内容远远不止这些。不过一个视频也讲不了太多东西,讲的还是数学,也是没有办法。一边看题一边说吧。 辗转…...

“目标检测”任务基础认识
“目标检测”任务基础认识 1.目标检测初识 目标检测任务关注的是图片中特定目标物体的位置。 目标检测最终目的:检测在一个窗口中是否有物体。 eg:以猫脸检测举例,当给出一张图片时,我们需要框出猫脸的位置并给出猫脸的大小,如…...

springboot+vue的宠物咖啡馆平台(前后端分离)
博主主页:猫头鹰源码 博主简介:Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战,欢迎高校老师\讲师\同行交流合作 主要内容:毕业设计(Javaweb项目|小程序|Pyt…...

LaWGPT—基于中文法律知识的大模型
文章目录 LaWGPT:基于中文法律知识的大语言模型数据构建模型及训练步骤两个阶段二次训练流程指令精调步骤计算资源 项目结构模型部署及推理 LawGPT_zh:中文法律大模型(獬豸)数据构建知识问答模型推理训练步骤 LaWGPT:基…...

一文弄明白KeyedProcessFunction函数
引言 KeyedProcessFunction是Flink用于处理KeyedStream的数据集合,它比ProcessFunction拥有更多特性,例如状态处理和定时器功能等。接下来就一起来了解下这个函数吧 正文 了解一个函数怎么用最权威的地方就是 官方文档 以及注解,KeyedProc…...

alibabacloud学习笔记06(小滴课堂)
讲Sentinel流量控制详细操作 基于并发线程进行限流配置实操 在浏览器打开快速刷新会报错 基于并发线程进行限流配置实操 讲解 微服务高可用利器Sentinel熔断降级规则 讲解服务调用常见的熔断状态和恢复 讲解服务调用熔断例子 我们写一个带异常的接口:...

Code Composer Studio (CCS) - Licensing Information
Code Composer Studio [CCS] - Licensing Information 1. Help -> Code Composer Studio Licensing Information2. Upgrade3. Specify a license fileReferences 1. Help -> Code Composer Studio Licensing Information 2. Upgrade 3. Specify a license file …...

uniapp引入微信小程序直播组件
方法1.小程序跳转视频号直播 微信小程序跳转到视频号 1.1微信开放平台注册 https://open.weixin.qq.com/ 2.2 方法2.使用小程序提供的直播组件 参考 微信小程序跳转视频号直播 小程序直播官方文档 https://developers.weixin.qq.com/miniprogram/dev/component/live-play…...
五个简单的C#编程案例
案例一:Hello, World! csharp using System; class Program { static void Main() { Console.WriteLine("Hello, World!"); } } 这个案例是最基础的C#程序,它打印出“Hello, World!”到控制台。每个C#程…...

Zlibrary低调官宣2024年最新网址,国内可直接访问,免费下载海量电子书籍
最近过节,文章也没怎么写,明天要上班了,今天写篇文章做个预热。 春节期间,“知识大航海”群里,有位群友分享了一个Zlibrary的最新地址,感谢这位群友妹妹的热心分享,这个地址国内可以直接访问。 …...
Android 开机启动
一、添加权限 <uses-permission android:name"android.permission.RECEIVE_BOOT_COMPLETED"/> 二、写一个广播接收器 public class BootReceiver extends BroadcastReceiver {Overridepublic void onReceive(Context context, Intent intent) {if(Intent.ACT…...
二叉树相关算法需了解汇总-基础算法操作
文章目录 144.二叉树的前序遍历145.二叉树的后序遍历94.二叉树的中序遍历102.二叉树的层序遍历107.二叉树的层次遍历倒序199.二叉树的右视图637.二叉树的层平均值429.N叉树的层序遍历515.在每个树行中找最大值116.填充每个节点的下一个右侧节点指针104.二叉树的最大深度111.二叉…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...