基于Netty连接池泄露问题了解客户端启动源码

连接池导致内存泄漏案例演示

简介

我们生产环境常常会用Netty客户端作为连接工具,尽管Netty强大且方便,但是使用不当的话也可能造成严重的生成事故。笔者本文就以一个连接池使用不当导致内存泄漏的案例来展开探讨。

问题复现

服务端代码

我们先贴出服务端代码,代码非常简单,就是启动,然后处理客户端请求。

public class Server {public static void main(String[] args) {NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);NioEventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100).handler(new LoggingHandler()).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new LoggingHandler());}});ChannelFuture channelFuture = bootstrap.bind(9999).sync();//channelFuture.channel().closeFuture().sync();channelFuture.channel().closeFuture().addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {System.out.println(">>>>>>>>>>>>>>>链路关闭!");bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}});/*TimeUnit.SECONDS.sleep(4);channelFuture.channel().close();*/} catch (Exception e) {e.printStackTrace();} finally {/*bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();*/}}
}

服务端代码

服务端代码如下,这里笔者用poolSize来模拟客户端并发请求,通过传入的poolSize创建poolSize个客户端和服务端建立连接。

public class Client {static void initPool(int poolSize) {for (int i = 0; i < poolSize; i++) {NioEventLoopGroup bossGroup = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(bossGroup).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)//.handler(new LoggingHandler()).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();}});ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 9999).sync();channelFuture.channel().closeFuture().addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {System.out.println(">>>>>>>>>>>>>>>链路关闭!");//bossGroup.shutdownGracefully();channelFuture.channel().close();}});} catch (Exception e) {e.printStackTrace();}}}}

完成上述编码之后,我们编写启动代码,可以看到笔者这里启动了200个并发请求。

public static void main(String[] args) {try {TimeUnit.SECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}initPool(200);System.out.println(">>>>>>>>>>>>连接池创建成功!");}

启动并重现问题

完成编码工作之后,我们先把服务端启动,然后为了更快的重现问题,我们在启动客户端之前,需对客户端配置如下参数调整堆区

-Xmn32m -Xmx32m

启动后不久,我们就发现客户端并发请求服务端时,抛出内存泄漏的错误

Exception in thread "nioEventLoopGroup-23-1" Exception in thread "nioEventLoopGroup-28-1" Exception in thread "nioEventLoopGroup-28-16" java.lang.OutOfMemoryError: Java heap space
java.lang.OutOfMemoryError: GC overhead limit exceeded
Exception in thread "RMI TCP Connection(idle)" Exception in thread "nioEventLoopGroup-28-8" java.lang.OutOfMemoryError: GC overhead limit exceeded
java.lang.OutOfMemoryError: GC overhead limit exceeded
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat io.netty.channel.nio.SelectedSelectionKeySet.<init>(SelectedSelectionKeySet.java:29)at io.netty.channel.nio.NioEventLoop.openSelector(NioEventLoop.java:207)at io.netty.channel.nio.NioEventLoop.<init>(NioEventLoop.java:149)at io.netty.channel.nio.NioEventLoopGroup.newChild(NioEventLoopGroup.java:127)at io.netty.channel.nio.NioEventLoopGroup.newChild(NioEventLoopGroup.java:36)at io.netty.util.concurrent.MultithreadEventExecutorGroup.<init>(MultithreadEventExecutorGroup.java:84)at io.netty.util.concurrent.MultithreadEventExecutorGroup.<init>(MultithreadEventExecutorGroup.java:58)at io.netty.channel.MultithreadEventLoopGroup.<init>(MultithreadEventLoopGroup.java:52)at io.netty.channel.nio.NioEventLoopGroup.<init>(NioEventLoopGroup.java:87)at io.netty.channel.nio.NioEventLoopGroup.<init>(NioEventLoopGroup.java:82)at io.netty.channel.nio.NioEventLoopGroup.<init>(NioEventLoopGroup.java:63)at io.netty.channel.nio.NioEventLoopGroup.<init>(NioEventLoopGroup.java:51)at io.netty.channel.nio.NioEventLoopGroup.<init>(NioEventLoopGroup.java:43)at com.mx.tuning.case2.Client.initPool(Client.java:29)at com.mx.tuning.case2.Client.main(Client.java:70)
Exception in thread "nioEventLoopGroup-26-1" java.lang.OutOfMemoryError: GC overhead limit exceeded

连接池泄漏原因详解

我们打开jvisualvm查看客户端线程信息,可以看到客户端创建无数个独立的NioEventLoopGroup。

在这里插入图片描述

查看server却发现,都是使用同一个NioEventLoopGroup,每个请求都是通过NioEventLoopGroup中的一个线程去处理的。

在这里插入图片描述

很明显造成客户端连接池泄漏的原因,就是我们错用的线程池,我们每个客户端发起请求时用的都是各自的NioEventLoopGroup,这不仅使得连接池没有复用,更使得nio模型用起来和bio没有区别。

在这里插入图片描述

对此,我们不妨看看NioEventLoopGroup的源码,先从构造方法入手,可以看到默认情况下会调用一个 this(0);

 public NioEventLoopGroup() {this(0);}

经过我们的不断步入,即可发现默认情况下EventLoopGroup会创建DEFAULT_EVENT_LOOP_THREADS 个线程

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);}

我们不妨通过源码看看DEFAULT_EVENT_LOOP_THREADS 的线程数。如下图,默认情况下,线程池数是CPU核心数的2倍。

在这里插入图片描述

以笔者为例,笔者的CPU核心数为16,那么最终结果则是32。

在这里插入图片描述

这一点,我们完全可以将Netty源码的结果输出打印出来

System.out.println("thread num="+Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)));

可以看到结果确实是32

在这里插入图片描述

完成线程数初始化的工作之后,源码就会为ThreadPerTaskExecutor初始化对应个数的执行器处理后续的各种异步任务。

在这里插入图片描述

解决方案

可以想到一共200个请求都会创建32个线程,在32m的内存空间是多么可怕的一件事,所以我们不妨对客户端做一个调整,使得每一个请求都可以复用一个NioEventLoopGroup。

改造后的代码如下所示,可以看到笔者将循环建立到连接操作上,这样一来就确保的所有的客户端请求都复用一个NioEventLoopGroup。

在这里插入图片描述

再次启动我们就发现客户端所有连接都成功了,使用监控工具查看线程池也没有问题,线程数确实是CPU核心数的2倍。

在这里插入图片描述

控制台也输出连接成功了。

在这里插入图片描述

补充注意事项

这里我们补充一个注意事项,可以看到笔者对每一个请求结束后的关闭方法并不是将bossGroup关闭,而是将每个客户端对应的管道即channelFuture.channel()关闭。这样一来,我们也确保了一个连接报错之后,不会将其他连接对应的NioEventLoopGroup关闭了。

在这里插入图片描述

更进一步:客户端连接源码详解

Netty客户端创建原理

图解

了解了客户端连接池的错误使用案例之后,我们不妨对客户端创建进行进一步的了解,先来看看下面这张经典的时序图,可以看到Netty客户端建立连接的方式大抵是以下几个步骤:

  1. 创建客户端
  2. 构建NIO线程组,这也就是我们说的创建NioEventLoopGroup
  3. 反射创建频道
  4. 创建频道流水线管理要处理的管道
  5. 上述初始化完成之后,通过异步的方式发起TCP连接
  6. 然后异步处理连接
  7. 连接操作成功后发起连接操作结果事件
  8. 调用我们编写的业务代码的handler

在这里插入图片描述

源码验证

这里我们不妨对几个比较核心的步骤通过源码的方式进行一下分析,创建NioEventLoopGroup这一步我们就不多说了,上文已经说明了,感兴趣的读者可以自行进一步查看源码。

然后就是反射创建频道,这一步在源码中的这个位置,可以看到这个代码我们会将管道的类传入

在这里插入图片描述

步入源码,我们也能发现这块代码会通过一个反射工厂完成频道的创建。

在这里插入图片描述

后续频道流水线初始化这里不是重点,我们这里简单查看一下即可

在这里插入图片描述

服务端流水线操作也是同理,通过addLast方法来编排频道的排序。

在这里插入图片描述

自此我们将客户端创建的流程整体有了整体的了解,下面我们不妨进一步了解一下客户端连接的工作流程。

Netyy客户端注册源码详解

上文我们大概的过了初始化配置的源码,接下来我们就来了解一下bootstrap连接服务器的原理。我们首先将服务端代码启动。

然后调整一下客户端启动代码,再将其启动

 public static void main(String[] args) {initPool(1);System.out.println(">>>>>>>>>>>>连接池创建成功!");}

然后我们在connect处插入断点,并将客户端代码启动

在这里插入图片描述

此时,不断步入,我们的代码走到了Bootstrap的connect方法,启动有一个doResolveAndConnect,我们不妨步入查看详情。

 public ChannelFuture connect(SocketAddress remoteAddress) {......return doResolveAndConnect(remoteAddress, config.localAddress());}

然后代码会走到一个initAndRegister方法,该方法完成之后会生成一个ChannelFuture 的异步任务,任务发起后,后续代码会注册一个监听器,监听注册结果以及根据注册结果发起真正的远程连接代码,我们先不妨看看异步任务生成的逻辑方法initAndRegister为我们做了些什么。

 private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {final ChannelFuture regFuture = initAndRegister();
....if (regFuture.isDone()) {..........} else {......//上述异步注册任务发起后,设置一个监听器,一旦上述注册任务完成就会执行监听器中的doResolveAndConnect0方法发起连接regFuture.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {// Directly obtain the cause and do a null check so we only need one volatile read in case of a// failure.Throwable cause = future.cause();if (cause != null) {......} else {//发起客户端连接服务端doResolveAndConnect0promise.registered();doResolveAndConnect0(channel, remoteAddress, localAddress, promise);}}});return promise;}}

可以看到该方法内部为调用一个register方法,我们不妨看看这个register做了些什么。

 final ChannelFuture initAndRegister() {Channel channel = null;.....ChannelFuture regFuture = config().group().register(channel);......}

我们不断这深入这段代码,可以看到AbstractChannel中的register会通过eventLoop提交一个register0的任务,我们不妨看看eventLoop的execute方法做了些什么。

 @Overridepublic final void register(EventLoop eventLoop, final ChannelPromise promise) {AbstractChannel.this.eventLoop = eventLoop;if (eventLoop.inEventLoop()) {.....} else {try {eventLoop.execute(new Runnable() {@Overridepublic void run() {register0(promise);}});} catch (Throwable t) {......}}}

可以看到execute方法,执行了两段核心代码:

  1. addTask,即将我们register0这个任务提交到队列中
  2. 调用startThread启动NioEventLoop获取并运行队列中的任务。

所以我们不妨看看startThread是如何启动线程去执行我们的register这个任务的。

@Overridepublic void execute(Runnable task) {....//添加任务到队列中addTask(task);if (!inEventLoop) {// 如果NioEventLoop没启动,则启动让其跑任务startThread();if (isShutdown() && removeTask(task)) {reject();}}......}

可以看到startThread方法调用了doStartThread去启动线程,我们不妨看看它做了些什么。

private void startThread() {if (state == ST_NOT_STARTED) {if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {try {doStartThread();} catch (Throwable cause) {......}}}}

由于代码比较长,笔者这里便使用图片的形式来展示一下代码,可以看到这段代码中会通过threadPerTaskExecutor提交一个任务,该任务便是获取任务中的线程然后,调用 SingleThreadEventExecutor.this.run();方法,读者如果查看一下this对象即可发现,这个this就是我们的NioEventLoop,我们不妨看看run方法内部做了些什么。

在这里插入图片描述

步入源码后,我们会发现该run方法是一个无限的for循环,会获取本次通到事件的key

在这里插入图片描述

因为我们是初次建立连接,所以代码走到了这里,内部没有做任务事情,所以笔者直接略过这段代码。然后就走到了下图的runAllTasks

在这里插入图片描述

最终代码就会从队列中取出我们的register0的任务,并执行。

在这里插入图片描述

由此走向了一个闭环。

在这里插入图片描述

自此,我们在这里做一个小结,整个任务的流程:

  1. 调用connect方法。
  2. 调用initAndRegister封装并提交一个register0的异步任务。
  3. 添加上述任务到队列中。
  4. 启动NioEventLoop线程去处理提交到队列中的任务。
  5. NioEventLoop获取通到事件,得到上述的register0。
  6. 执行register0。

Netty注册源码核心异步执行流程

上文中我们代码最终走到了register0,我们不妨看看register0做了些什么。经过笔者的整理,可以看到该方法大抵做了3个步骤:

  1. 调用doRegister
  2. 流水线注册新的handler即调用invokeHandlerAddedIfNeeded
  3. 对promise发起连接注册成功后的通知,即调用safeSetSuccess方法。

所以我们不妨看看doRegister做了些什么

private void register0(ChannelPromise promise) {try {.......doRegister();
.......// user may already fire events through the pipeline in the ChannelFutureListener.pipeline.invokeHandlerAddedIfNeeded();safeSetSuccess(promise);pipeline.fireChannelRegistered();.........}} catch (Throwable t) {......}}

可以看到doRegister会调用register,最终生成一个通到事件的key,赋值给selectionKey ,我们不妨看看生成key之前register方法做了些什么?

@Overrideprotected void doRegister() throws Exception {boolean selected = false;for (;;) {try {selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);return;} catch (CancelledKeyException e) {........}}}

然后我们就来到了register方法,因为是第一次注册,所以k的值为null,代码走到register执行事件注册一个连接事件,然后返回这个key。

public final SelectionKey register(Selector sel, int ops,Object att)throws ClosedChannelException{synchronized (regLock) {.......SelectionKey k = findKey(sel);if (k != null) {.....}if (k == null) {.....synchronized (keyLock) {if (!isOpen())throw new ClosedChannelException();k = ((AbstractSelector)sel).register(this, ops, att);addKey(k);}}return k;}}

自此我们的doRegister即注册都完成了,继续进行执行后续步骤,通知客户端可以真正开始连接了。

在这里插入图片描述

可以看到,代码走到了promise的trySuccess方法,我们不妨步入看看这个方法为我们做了些什么

protected final void safeSetSuccess(ChannelPromise promise) {if (!(promise instanceof VoidChannelPromise) && !promise.trySuccess()) {logger.warn("Failed to mark a promise as success because it is done already: {}", promise);}}

可以看到该段代码会调用一个名为notifyListeners区通知上文注册register监听器。

public boolean trySuccess(V result) {if (setSuccess0(result)) {notifyListeners();return true;}return false;}

查看该通知方法,我们可以看到调用了notifyListener0方法。

在这里插入图片描述

然后代码就走到了DefaultPromise的notifyListener0方法,它将会调用operationComplete方法,他将会将注册状态设置为true,并让客户端执行连接方法,我们不妨步入看看

private static void notifyListener0(Future future, GenericFutureListener l) {try {l.operationComplete(future);} catch (Throwable t) {.....}}}

可以看到,代码最终走到了我们一开始所说的哪个异步任务的后续注册监听的内部逻辑代码中,Bootstrap的doResolveAndConnect ,它做了以下两件事:

  1. 将注册结果设置为true,即调用 promise.registered();
  2. 执行连接逻辑doResolveAndConnect0,我们不妨看看内部执行细节。

在这里插入图片描述

可以看到其内部核心步骤就是调用doConnect方法。

在这里插入图片描述

同理,它也是向eventLoopGroup提交一个连接的异步任务,如下图,这里的execute仍然会执行我们上述步骤中的addTask,然后NioEventLoop轮询通到事件的过程。

在这里插入图片描述

之后代码就执行到上图的channel.connect(remoteAddress, connectPromise);

在这里插入图片描述

然后这个连接方法就会执行doConnect将OP_CONNECT注册到通到事件中。

 @Overrideprotected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {if (localAddress != null) {doBind0(localAddress);}boolean success = false;try {boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);if (!connected) {selectionKey().interestOps(SelectionKey.OP_CONNECT);}success = true;return connected;} finally {if (!success) {doClose();}}}

最终NioEventLoop轮询到上述所说的注册的感兴趣的连接事件,完成finishConnect工作,自此所有连接工作完成。

在这里插入图片描述

小结

自此我们的整个客户端连接工作就完成了,我们不妨小结整个流程:

在这里插入图片描述

参考

Java性能调优 6步实现项目性能升级

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Sui承诺向流动性质押协议投入$SUI

Sui将提供SUI以支持三个流动性质押协议及其相应的流动性质押token&#xff08; Liquid Staking Tokens&#xff0c;LST&#xff09;&#xff0c;为网络上不断增长的DeFi领域增加了流动性。此次注入将加强LST在交易和其他DeFi 用途中的流动性。 流动性质押让SUI所有者通过将其t…

32、应急响应——linux

文章目录 一、linux进程排查二、linux文件排查三、linux用户排查四、linux持久化排查4.1 历史命令4.2 定时任务排查4.3 开机启动项排查 五、linux日志分析六、工具应用 一、linux进程排查 查看资源占用&#xff1a;top查看所有进程&#xff1a;ps -ef根据进程PID查看进程详细信…

ARM开发

ARM课程介绍 课程特点 ARM开发 --> Linux移植 --> 驱动开发 前后联系&#xff1a;ARM和系统移植为驱动开发学习做准备工作 所需知识&#xff1a;C语言基础及STM32需要的硬件知识 课程要求 目标&#xff1a;学习程序运行原理、硬件的控制原理 会看原理图、芯片手册、学习…

ChatGPT对话为什么不用WebSocket而使用EventSource?

文章目录 1. 引言2. WebSocket和EventSource简介2.1 WebSocket2.2 EventSource 3. ChatGPT对话系统的特点4. EventSource的优势4.1 简单易用4.2 容错性强4.3 兼容性良好 5. 为何选择EventSource而非WebSocket&#xff1f;5.1 单向通信模式5.2 长轮询模式5.3 简化部署和维护 6. …

SpringBoot+Netty+Websocket实现消息推送

这样一个需求&#xff1a;把设备异常的状态每10秒推送到页面并且以弹窗弹出来&#xff0c;这个时候用Websocket最为合适&#xff0c;今天主要是后端代码展示。 添加依赖 <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifact…

用Go汇编实现一个快速排序算法

本代码全网首发&#xff0c;使用Go plan9 windows arm64汇编&#xff0c;实现基础版快速排序算法。 未引入随机因子的快速排序的普通Go代码长这样。 func QuickSort(arr []int) {if len(arr) < 1 {return}base, l, r : arr[0], 0, len(arr)-1for i : 1; i < r; {if arr…

Vue H5项目,怎么引入uni.webview sdk,调用uni postMessage实现手机蓝牙连接打印功能(uniapp)

前言 目前公司Vue H5项目&#xff0c;用webview打包成APP&#xff0c;现产品提出这样打包出来的app运行较慢&#xff0c;需要用uniapp方式&#xff08;即使用HBuilder编辑器来打包H5&#xff09;来打包&#xff0c;那需要的基座就不是安卓的基座而是uniapp的基座&#xff0c;而…

扭矩法、屈服点法哪个比较高效?——SunTorque智能扭矩系统

在机械制造和维修领域&#xff0c;拧紧螺栓和螺母是一项重要的操作。拧紧方法的合理选择和使用&#xff0c;对于确保机械设备的稳定性和安全性具有至关重要的作用。本文SunTorque智能扭矩系统将介绍两种最常用的拧紧方法&#xff0c;并探讨它们的轴力范围计算方法。一、扭矩法 …

电信网关配置管理系统后台 upload.php 文件上传漏洞复现

0x01 产品简介 中国电信集团有限公司(英文名称“China Telecom”、简称“中国电信”)成立于2000年9月,是中国特大型国有通信企业、上海世博会全球合作伙伴。 0x02 漏洞概述 电信网关配置管理系统后台 /manager/teletext/material/upload.php 接口存在文件上传漏洞,攻击者…

【数电笔记】56-消抖开关

目录 说明&#xff1a; 1. 按键抖动形成的原因 2. 按键消抖的方法 3. 用与非RS触发器构成消抖开关&#xff08;硬件消抖&#xff09; 说明&#xff1a; 笔记配套视频来源&#xff1a;B站本系列笔记并未记录所有章节&#xff0c;只对个人认为重要章节做了笔记&#xff1b;标…

【稳定检索|投稿优惠】2024年艺术鉴赏与社会科学教育国际会议(ICAASSE 2024)

2024年艺术鉴赏与社会科学教育国际会议(ICAASSE 2024) 2024 International Conference on Art Appreciation and Social Science Education(ICAASSE) 一、【会议简介】 2024年艺术鉴赏与社会科学教育国际会议(ICAASSE 2024)&#xff0c;这场学术盛宴&#xff0c;将于2024年2月1…

郝斌C语言自学教程笔记

赫斌C语言——笔记目录 c语言编程预备知识流程控制函数变量指针结构体位运算符 前段时间康哥看我C语言基础不牢,推荐我学习郝斌老师的C语言课程&#xff0c;花2周看完之后发现确实是目前所看的C语言课程中最好的&#xff0c;不仅非常适合入门&#xff0c;而且对即使学了几年C语…

怒斥以色列后突发心脏病倒地,土耳其议员抢救无效身亡!

这两天互联网上热传一段视频&#xff0c;说的就是土耳其议员在议会演讲时突然倒地晕厥&#xff0c;两天后就去世了。这可真是让人震惊啊&#xff01; 据说这位议员是土耳其反对党幸福党的&#xff0c;名字叫比特梅兹。他在议会发表批评以色列的言论时&#xff0c;情绪过于激动…

安装2023最新版Java SE 21.0.1来开发Java应用程序

安装2023最新版Java SE 21.0.1来开发Java应用程序 Install the latest version of Java SE 21.01 to Develop Java Applications By JacksonML 本文简要介绍如何下载和安装2023年最新版Java Development Kit (简称JDK&#xff0c;即Java开发工具包标准版&#xff09;21.0.1&…

长尾问题之LDAM

做法&代码&公式 step1: 全连接层的权重W和特征向量X都归一化,相乘 W * X P (得到各个类别的概率) # 定义权重&#xff0c;初始化 weight nn.Parameter(torch.FloatTensor(num_classes, num_features)) weight.data.uniform_(-1, 1).renorm_(2, 1, 1e-5).mul_(1e5)#…

Java 线程的基本概念

创建和运行线程 方法一&#xff0c;直接使用 Thread // 创建线程对象 Thread t new Thread() {public void run() {// 要执行的任务}};// 启动线程 t.start();例如&#xff1a; // 构造方法的参数是给线程指定名字&#xff0c;推荐 Thread t1 new Thread("t1") …

网络安全——SQL注入实验

一、实验目的要求&#xff1a; 二、实验设备与环境&#xff1a; 三、实验原理&#xff1a; 四、实验步骤&#xff1a; 五、实验现象、结果记录及整理&#xff1a; 六、分析讨论与思考题解答&#xff1a; 七、实验截图&#xff1a; 一、实验目的要求&#xff1a; 1、…

《Cadence 16.6电路设计与仿真从入门到精通》——1.4 Cadence SPB 16.6的启动

《Cadence 16.6电路设计与仿真从入门到精通》——1.4 Cadence SPB 16.6的启动  2017-05-027334 版权 简介: 本节书摘来自异步社区《Cadence 16.6电路设计与仿真从入门到精通》一书中的第1章,第1.4节,作者: 王超 , 胡仁喜等 更多章节内容可以访问云栖社区“异步社区”公…

《PCL多线程加速处理》-滤波-统计滤波

《PCL多线程加速处理》-滤波-统计滤波 一、效果展示二、实现方式三、代码一、效果展示 提升速度随着点云越多效果越明显 二、实现方式 1、原始的统计滤波实现方式 #include <pcl/filters/statistical_outlier_removal.h>pcl::PointCloud<pcl::PointXYZ

使用 Python 使用贝叶斯神经网络从理论到实践

一、说明 在本文中&#xff0c;我们了解了如何构建一个机器学习模型&#xff0c;该模型结合了神经网络的强大功能&#xff0c;并且仍然保持概率方法进行预测。为了做到这一点&#xff0c;我们可以构建所谓的贝叶斯神经网络。 这个想法不是优化神经网络的损失&#xff0…