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

【JavaEE】 多线程-初阶

多线程-初阶

1. 认识线程

1.1 概念

1) 线程是什么
  • 多个线程组成了一个进程,线程好比是一跟光纤中的一个玻璃丝,进程是整根光纤。

  • 一个进程中的线程共享这个进程中的资源(内存、硬盘)

2) 为什么需要线程

单核CPU发展出现瓶颈,想要再提高算力,只能增加CPU个数,并发编程就是利用多核CPU的绝佳方式.

使用进程也可以实现并发编程,只是进程重量大,创建销毁消耗资源多, 所以更好的方式是使用线程进行并发编程.

3) 线程和进程的区别
  • 线程包含于进程
  • 每个进程至少有一个线程, 即main线程(main thread)
  • 进程之间互不干扰, 但是线程之间耦合度高(一个线程出现问题, 其他线程也会崩溃)
  • 进程是系统分配资源的最小单位, 线程是系统调度的最小单位
4) Java中线程 和 操作系统线程 的关系

Java中线程是对于操作系统线程的封装和抽象.


1.2 第一个多线程程序

public class Main {public static void main(String[] args) {Runnable r1 = new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello thread1.");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};Thread t1 = new Thread(r1);Runnable r2 = new Runnable() {@Overridepublic void run() {while (true) {System.out.println("hello thread2.");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}}}};Thread t2 = new Thread(r2);t1.start();t2.start();}
}

运行结果:

hello thread1.
hello thread2.
hello thread1.
hello thread2.
hello thread1.
hello thread2.
hello thread2.
hello thread1.
hello thread1.
hello thread2.

1.3 创建线程

1) 继承Thread
class MyThread extends Thread{public void run() {System.out.println("继承Thread得到");}
}
public class Demo1 {public static void main(String[] args) {MyThread t1 = new MyThread();t1.start();}
}

运行结果:

继承Thread得到
2) 实现Runnable接口
class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("实现Runnable接口得到");}
}
public class Demo2 {public static void main(String[] args) {MyRunnable r1 = new MyRunnable();Thread t1 = new Thread(r1);t1.start();}
}

运行结果:

实现Runnable接口得到

继承Thread和实现Runnable接口的this指代的对象不同, 前者直接指代这个线程, 后者指代接口, 想要指代线程需要使用Thread.currentThread().

4) 使用匿名内部类
public class Demo3 {public static void main(String[] args) {Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("使用Thread匿名内部类,直接传参new Runnable接口得到");}});Thread t2 = new Thread() {public void run() {System.out.println("使用Thread匿名内部类,直接重写run方法得到");}};t1.start();t2.start();}
}

运行结果:

使用Thread匿名内部类,直接传参new Runnable接口得到
使用Thread匿名内部类,直接重写run方法得到
5) 使用lamda表达式
public class Demo4 {public static void main(String[] args) {Thread t = new Thread(()-> {// 不需要重写run,lamda表达式就相当于是run方法System.out.println("lamda表达式创建得到");});t.start();}
}

运行结果:

lamda表达式创建得到

1.4 使用多线程编程可以增加程序的运行速度

但是可能导致程序线程不安全, 需要合理加锁.


2. Thread类常见方法

2.1 Thread常见构造方法

构造方法名说明
Thread()普通构造(仅分配空间)
Thread(Runnable)根据所给的run()构造对象
Thread(String)为将构造出的线程进行命名
Thread(Runnable, String)根据run()创建对象并命名

命名主要是为了方便调试.


2.2 Thread常见属性

方法名作用
start()创建线程并运行
getId()返回线程的Id
(这个Id不同于操作系统未进程分配的Id,也不是PCB中的Id,仅仅是JVM分配的Id)
getName()返回线程名字
getPriority()返回优先级
getState()返回线程目前的状态(NEW, RUNNABLE, WAITING, TIMED_WAITING,BLOCKED,TERMINATED)
isDaemon()判断是否为后台进程(后台进程不决定一个线程的存亡,只有前台进程才决定)
isAlive()判断是否存活
isInterrupted()判断是否被中断

2.3 让一个Thread跑起来

使用start()方法即可使其开始运行.

之前写过的run方法, 只是为这个线程规定要怎么做, 只有start方法才能启动线程.

2.3.1 start 和 run 的区别

start会调用系统api进行创建线程

run只是一个普通的方法,告诉线程的执行逻辑,不会创建线程

2.4 中断一个线程

有两种方式:

  • 设置一个记号, 线程A线程B共享这个记号, 两个线程约定一个在其为true时工作, 一个在其为false时工作, 此时如果在A中对于这个记号进行更改, 那就能够使得B停止工作.
public class Demo5 {// 设置共同变量public static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{System.out.println("t1");// 在第一个线程执行完后暂停3秒try {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}// 3秒后,设置共同变量为falseflag = false;});Thread t2 = new Thread(()->{while (flag == true) {System.out.println("t2");}});t1.start();// 在第二个线程执行前暂停2秒,让t1线程运行2秒Thread.sleep(2000);// 意味着t2只能执行1秒t2.start();}
}

运行结果:

t1
(等待3)
t2
t2
...
t2
t2

在3秒后,t1线程将共享变量修改为false, 所以t2被中断.


  • 调用interrupt()进行通知
public class Demo6 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while (!Thread.interrupted()) {System.out.println("t1尚未被中断");try {Thread.sleep(100);} catch (InterruptedException e) {System.out.println("t1收到中断信号");throw new RuntimeException(e);}break;}});System.out.println(t1.getState());t1.start();System.out.println(t1.getState());// 暂停2秒后进行中断Thread.sleep(1);t1.interrupt();}
}

运行结果:

NEW
RUNNABLE
t1尚未被中断
t1收到中断信号
Exception in thread "Thread-0" java.lang.RuntimeException: java.lang.InterruptedException: sleep interruptedat Demo6.lambda$main$0(Demo6.java:10)at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.InterruptedException: sleep interruptedat java.lang.Thread.sleep(Native Method)at Demo6.lambda$main$0(Demo6.java:7)... 1 more

interrupted() 和 currentThread().isInterrupted() 截然不同:

方法名说明
interrupted()查看当前线程是否被中断, 清除标记为false
currentThread().isInterrupted()查看当前线程是否中断,仅作判断, 不清除标记

演示:

public class Demo7 {public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0; i < 10; i++) {//System.out.println(Thread.interrupted());System.out.println(Thread.currentThread().isInterrupted());}});t1.start();t1.interrupt();}
}运行结果:
true
true
true
true
true
true
true
true
true
true
//(这种方法不清除中断标记, 仅作判断)
public class Demo7 {public static void main(String[] args) {Thread t1 = new Thread(()->{for (int i = 0; i < 10; i++) {System.out.println(Thread.interrupted());}});t1.start();t1.interrupt();}
}运行结果:
true
false
false
false
false
false
false
false
false
//(这种方法清除中断标记, 恢复为未被中断状态)

2.5 等待一个线程

线程执行有先后顺序的时候**(线程A的执行需要依赖于线程B的执行结果), **那就需要使用join()方法, 这个方法能够保护当前的线程执行完毕后,其他线程才会去执行.

// Press Shift twice to open the Search Everywhere dialog and type `show whitespaces`,
// then press Enter. You can now see whitespace characters in your code.
public class Main {public static int count = 0;// 1public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()-> {System.out.println("t11");for (int i = 0; i < 50000; i++) {count++;}System.out.println("t11");});Thread t2 = new Thread(()-> {System.out.println("t21");for (int i = 0; i < 50000; i++) {count++;}System.out.println("t21");});t1.start();t1.join();t2.start();Thread.sleep(100);System.out.println(count);}
}

在都对count进行++五万次的操作中,可以不加锁,也能使得count得到预期值的方法就是让t2在t1执行结束之后才启动,这样两个线程都能完成自己的任务,得到预期count。

2.5.1 方法中不能够加public、static等修饰词

访问局部变量的过程:对象-> 方法->局部变量。访问局部变量就已经有了访问权限的设定了。由此加修饰符也成了摆设。
对应static来说,因为static只能修饰成员变量和成员方法,在局部变量中用static修饰,又不能直接被类调用。

2.6 获取当前线程的引用

使用 Thread.currentThread();进行获取。

2.7 休眠线程

使用 Thread.sleep(long mills)实现。


在线程内部需要捕获异常,在方法中使用需要抛出异常。

Thread t2 = new Thread(()-> {System.out.println("t21");for (int i = 0; i < 50000; i++) {count++;}// 捕获异常try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
}, "t2");
// 抛出异常
public static void main(String[] args) throws InterruptedException {

3. 线程的状态

3.1 线程的所有状态

NEW: 安排了工作, 还未开始行动

RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作.

BLOCKED: 这几个都表示排队等着其他事情

WAITING: 这几个都表示排队等着其他事情

TIMED_WAITING: 这几个都表示排队等着其他事情

TERMINATED: 工作完成了

使用isAlive()方法可以观察线程是否存活。

使用yield()方法会使线程重新排队。

4. 线程不安全

4.1 线程不安全发生的时机

在两个线程进行对于同一个变量进行修改的时候会出现线程不安全的问题。

4.2 线程不安全发生的原因

  1. 指令非原子性

    即使是“++”这个操作,仅有一条语句,也是由3条微指令构成的:

    1. 从内存中读出count到寄存器

    2. 在寄存器中完成++

    3. ++后的值放进寄存器。

    在多个线程的这三个操作如果相互穿插进行,那么就可能会读入”脏值“。

  2. 内存可见性

    内存可见性,一个线程对共享变量值的修改,能够及时地被其他线程看到.

    对于多次重复的读入同一个数据,编译器会对其进行优化,直接在寄存器中使用这份数据的拷贝值,不再从内存中进行读取,对这个变量的修改操作也都是在这个拷贝值身上完成,在这个线程使用完此变量后才会将最终值写进内存。

    这种方式对于单线程来说是一种优化,简便了数据的读取操作,但是对于多线程来说,如果在线程A频繁修改变量count的同时,

线程B需要对count进行修改,那么就会读到“脏值”。

4.2.1 解决内存可见性问题

使用volatile关键字,忽略编译器对其的优化。

5. synchronized 关键字——解决线程不安全问题

synchronized 会将其所在的代码块进行加锁。


5.1 synchronized 特性

1) 互斥

如果说一个代码块相当于是一间房,那么一个synchronized就相当于是给这个房间进行上锁,其他人想进去必须要等到里面的人把锁打开,两个人进行争夺房间的使用权的过程也称为“锁竞争”。

锁的作用就是让不同的线程拥有同一个对象的锁的时候,只有执行顺序靠前的线程能够正常运行,后面的线程需要等待前面的线程释放锁以后才能继续正常运行。

2)刷新内存
底层实现:

synchronized的底层是使用操作系统的mutex lock实现的.

synchronized工作过程本质上是通过获取一个安全的空间来进行保证操作原子性的:

  1. 获得互斥锁

  2. 从主内存拷贝变量的最新副本到工作的内存 3.

  3. 执行代码

  4. 将更改后的共享变量的值刷新到主内存

  5. 释放互斥锁

3) 可重入性
public static final Object locker = new Object();synchronized (locker) {synchronized (locker) {}
}

在对于一个对象上同一把锁两次的时候,理论上来说会产生“死锁”现象。

因为一个第二把锁所在的代码块执行的前提是第一把锁释放,但是第一把锁释放的条件是后序的代码块执行完,形成闭环,造成“死锁”。

死锁的成因

1)互斥使用:同一把锁的不同线程同一时间只有一个能够运行

2)不可抢占:后面的线程只能等前面的将锁释放后才能运行

3)循环等待:在A阻塞等待B释放锁的时候,B在等待A释放锁

4)请求保持:一个线程尝试获取多把锁(线程A在已经被锁1加上的情况下获取一个已经被占用的锁2,那么锁1不会被释放)

1和2都是锁的基本特性,3和4是代码结构,当同时满足以上四点的时候才会发生死锁。

5.2 synchronized 使用示例

1)给普通方法上锁

synchronized public void method1() {}

2)给静态方法上锁

synchronized public static void method1() {}

3)给代码块上锁

给当前对象上锁
//3
public void method2() {synchronized (this) {}
}
给类对象上锁
//4
public void method3() {synchronized (Demo2.class) {}
}

其中,3和4等价。

5.3 Java 标准库中的线程安全类

不安全的:ArrayList 、LinkedList、 HashMap、 TreeMap、 HashSet、 TreeSet、 StringBuilder

安全的:Vector (不推荐使用)、 HashTable (不推荐使用) 、ConcurrentHashMap、 StringBuffer

6. volatile关键字

6.1 volatile能够保证内存可见性

内存可见性,一个线程对共享变量值的修改,能够及时地被其他线程看到.

import java.util.Scanner;// volatile的作用
public class Demo3 {//public volatile static int isQuit = 0;public static int isQuit = 0;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()-> {while (isQuit == 0) {}System.out.println("t1退出");});t1.start();Thread.sleep(1000);Thread t2 = new Thread(()-> {System.out.println("请输入");Scanner scanner = new Scanner(System.in);isQuit = scanner.nextInt();});t2.start();Thread.sleep(1000);System.out.println(t1.getState());}
}

由于编译器的优化, t2对于isQuit变量进行修改并不影响t1线程中看到的isQuit变量是0, 这就叫做内存不可见.

但是如果加上volatile, 那么编译器会保证内存的可见性, 放弃优化.(所以会将代码的运行效率降低)

volatile的工作过程:

  1. 将内存中的数据放进寄存器
  2. 线程对于数据进行修改
  3. 将数据写回内存

如果是读取:

  1. 读取最新值进入工作内存
  2. 从工作内存中读取volatile变量的副本

6.2 volatile不能保证操作的原子性

volatile虽然一次性将数据读取到工作内存, 待其写完后又放回主内存, 但是在写的过程中, 如果其他线程也对同一个变量进行写入, 这将是合法的, 并且存在线程安全问题.

// 线程安全问题
public class Main {// 加上volatile并不能够得到预期的count值public static volatile int count = 0;// 1public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()-> {for (int i = 0; i < 50000; i++) {count++;}try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}});Thread t2 = new Thread(()-> {for (int i = 0; i < 50000; i++) {count++;}}, "t2");t1.start();t2.start();Thread.sleep(1000);System.out.println(count);}
}

结果:

75377

6.3 但是synchronized可以保证内存可见性和原子性

相关文章:

【JavaEE】 多线程-初阶

多线程-初阶 1. 认识线程 1.1 概念 1) 线程是什么 多个线程组成了一个进程&#xff0c;线程好比是一跟光纤中的一个玻璃丝&#xff0c;进程是整根光纤。 一个进程中的线程共享这个进程中的资源&#xff08;内存、硬盘&#xff09; 2) 为什么需要线程 单核CPU发展出现瓶颈…...

小程序OCR身份证识别

使用两种OCR识别&#xff1a;小程序和腾讯云 1.基于微信小程序OCR插件实现身份证拍照、上传并OCR识别的示例&#xff1a; 首先&#xff0c;在小程序中添加身份证拍照的功能&#xff0c;可以使用wx.chooseImage()选择照片并使用wx.uploadFile()上传&#xff0c;代码如下&#…...

【算法学习】归并算法Merge Sort总结

归并排序思路简单&#xff0c;速度仅次于快速排序&#xff0c;为稳定排序算法&#xff0c;一般用于对总体无序&#xff0c;但是各子项相对有序的数列。 1. 基本思想 归并排序使用分治思想&#xff0c;分治模式下每一层递归有三个步骤&#xff1a; 分解&#xff08;divide)&a…...

Swager如何使用

Swager是一个API文档自动生成工具&#xff0c;可以用于生成API接口文档&#xff0c;供开发者和用户查看和使用。它可以通过描述API接口的规范&#xff0c;自动生成API文档&#xff0c;使得API接口的发布和使用变得更加简单和规范。 下面是使用Swagger的步骤&#xff1a; 首先…...

DHorse v1.4.2 发布,基于 k8s 的发布平台

版本说明 优化特性 在集群列表增加集群版本&#xff1b;修改Jvm的GC指标名&#xff1b; 解决问题 解决shell脚本换行符的问题&#xff1b;解决部署历史列表页&#xff0c;环境名展示错误的问题&#xff1b;解决指标收集功能的异常&#xff1b; 升级指南 升级指南 DHorse…...

Java使用JJWT令牌

最近在B站大学学习Java开发&#xff0c;刚好学到登入验证&#xff0c;在使用JJWT令牌时踩了一些坑&#xff0c;在这里把代码和依赖给出&#xff0c;希望后来者得以借鉴。 依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api&l…...

“第四十四天”

这道题也不是难&#xff0c;但可能会忽略一种情况&#xff0c;当最大小出现在首位的时候&#xff0c;那个时候如果进行交换的话&#xff0c;大小值可能出现覆盖的情况&#xff0c;最终导致丢失最大值或者最小值&#xff0c;比如最大值 10 在第一位&#xff0c;最小值 0 随意&am…...

Unity Mono和.Net平台浮点算法的区别

static void TestFloat(){{//float speed2.0f/20;float speed 0.1f;float distance 2.0f;long needTime (long)(distance / speed);Log.Debug($"needTime{needTime}"); #if UNITY_EDITORif (needTime ! 19) #elseif (needTime ! 20)//.Net服务器和安卓手机 #endif…...

【SA8295P 源码分析 (二)】64 - QNX 与 Android GVM 显示 Dump 图片方法汇总

【SA8295P 源码分析】64 - QNX 与 Android GVM 显示 Dump 图片方法汇总 一、QNX侧1.1 surfacedump 功能1.2 screenshot 功能二、Android GVM 侧2.1 screencap -p 导出 PNG 图片2.2 screencap 不加 -p 参数,导出 RGB32 图片2.3 dumpsys SurfaceFlinger --display-id 方法系列文…...

shell命令以及运行原理和lLinux权限

shell命令以及运行原理 什么是shell shell是操作系统的外壳程序统称&#xff0c;我们是通过shell去和操作系统沟通的。 从技术角度&#xff0c;shell最简单的定义就是命令行解释器&#xff0c;主要包含两个功能&#xff1a; 将使用者的命令翻译给核心处理 将核心的处理结果…...

斯坦福JSKarel编程机器人使用介绍

斯坦福JSKarel编程机器人使用介绍 为了避免被编程语言固有的复杂性所困扰&#xff0c;有一个被称为卡雷尔&#xff08;Karel&#xff09;机器人的微型世界&#xff08;microworld&#xff09;的简化环境&#xff0c;可以让编程初学者从中学习理解编程的基本概念&#xff0c;而…...

SpringBoot中pom.xml不引入依赖, 怎么使用parent父项目的依赖

在Spring Boot项目中&#xff0c;如果你想使用父项目的依赖&#xff0c;而不想在pom.xml中显式引入依赖&#xff0c;你可以使用Maven的继承机制。 首先&#xff0c;确保你的Spring Boot项目是一个子项目&#xff0c;即它继承自一个父项目。要实现这一点&#xff0c;在pom.xml文…...

基于vue3+ts5+vue-router4+pinia2的PC端项目搭建教程

导语&#xff1a;在日常开发中&#xff0c;有时候会在项目中引入 ts 来解决一些 js 的问题&#xff0c;下面就简单介绍一下如何使用 vue3tsrouterpinia 来搭建一个项目。 目录 简介创建安装配置实战 简介 vue3 目前是常用的 vue 版本&#xff0c;提供了组合式 API 以及一些新…...

6个无版权、免费、高清图片素材库

找免费无版权图片素材&#xff0c;就上这6个网站&#xff0c;超高质量&#xff0c;可商用&#xff0c;赶紧收藏&#xff01; 1、菜鸟图库 https://www.sucai999.com/pic.html?vNTYwNDUx 网站主要为新手设计师提供免费素材&#xff0c;这些素材的质量都很高&#xff0c;类别也…...

什么是响应式设计?响应式设计的基本原理是什么?如何兼容低版本的 IE?

什么是响应式设计: 响应式设计&#xff08;Responsive Design&#xff09;是一种Web设计和开发方法&#xff0c;旨在使网站在不同设备和屏幕尺寸上都能提供一致的用户体验。响应式设计的目标是适应多种终端&#xff0c;包括桌面计算机、笔记本电脑、平板电脑和移动设备&#x…...

LeetCode 2906. 构造乘积矩阵【前后缀分解,数组】中等

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…...

vue3+koa+axios实现前后端通信

vue3koaaxios实现前后端通信 写了一个小demo来实现前后端通信,涉及跨域问题&#xff0c;非常简单可以给大家平时开发的时候参考 服务端&#xff1a; 目录结构如下&#xff1a; router index.js // router的入口文件 // 引入路由 const Router require("koa-router&quo…...

Required MultipartFile parameter ‘file‘ is not present

出现这个原因我们首先想到的是加一个RequestParam("file")&#xff0c;但是还有可能的原因是因为我们的名字有错误 <span class"input-group-addon must">模板上传 </span> <input id"uploadFileUpdate" name"importFileU…...

vue3后台管理系统之layout组件的搭建

1.1静态布局 <template><div class"layout_container"><!-- 左侧导航 --><div class"layout_slider"></div><!-- 顶部导航 --><div class"layout_tabbar"></div><!-- 内容展示区 --><…...

Minio 文件上传(后端处理同文件判断,同一文件秒传)

记录minio 文件上传 MinIO提供多个语言版本SDK的支持&#xff0c;下边找到java版本的文档&#xff1a; 地址&#xff1a;https://docs.min.io/docs/java-client-quickstart-guide.html maven依赖如下&#xff1a; XML <dependency><groupId>io.minio</groupId…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

Zustand 状态管理库:极简而强大的解决方案

Zustand 是一个轻量级、快速和可扩展的状态管理库&#xff0c;特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及&#xff0c;充电桩作为核心配套设施&#xff0c;其安全性与可靠性备受关注。然而&#xff0c;在高温、高负荷运行环境下&#xff0c;充电桩的散热问题与消防安全隐患日益凸显&#xff0c;成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论

路径问题的革命性重构&#xff1a;基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中&#xff08;图1&#xff09;&#xff1a; mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...