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

面试题分享之Java并发篇

注意:文章若有错误的地方,欢迎评论区里面指正 🍭 

系列文章目录

  • 面试题分享之Java集合篇(三)

  • 面试题分享之Java集合篇(二)

  • 面试题分享之Java基础篇(三)

前言

        今天给小伙伴们分享我整理的关于Java并发的一些常见面试题,这期涉及到线程的一些知识,所以要求小伙伴有一些操作系统的知识,不清楚也不要紧,也不是什么很难的知识点。🌈


一、什么是线程?什么是进程?

  • 线程:线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
  • 进程:进程是操作系统分配资源的基本单位,它是程序在计算机上的一次执行活动。当系统为一个程序分配资源后,该程序就成为一个独立的进程。

👨‍💻面试官追问线程跟进程的区别是什么?

  1. 线程是进程划分成的更小的运行单位。
  2. 独立性:进程是独立的,拥有独立的内存空间和系统资源;而线程是依赖进程的,多个线程共享其所属进程的内存空间和资源。
  3. 开销:进程的创建和销毁开销较大,因为需要为其分配和回收系统资源;而线程的创建和销毁开销较小。
  4. 切换速度:由于线程的上下文信息相对较少,因此线程间的切换速度通常比进程间的切换速度快。
  5. 通信与数据共享:进程间的通信和数据共享相对困难,需要通过特定的机制来实现;而线程间的通信和数据共享相对容易,因为它们共享其所属进程的内存空间和资源。
  6. 并发性:进程和线程都可以实现并发执行,但线程通常用于实现更细粒度的并发操作。在一个多核或多处理器的系统中,多个进程可以并行执行;而在一个进程中,多个线程也可以并行执行(如果处理器支持多线程)。

二、说一下线程的生命周期,它有几种状态

线程的生命周期包含五个阶段,即五种状态,分别是:

  1. 新建状态(New):新创建了一个线程对象,但还没有调用start()方法。在这个阶段,线程只是被分配了必要的资源,并初始化其状态。

  2. 就绪状态(Runnable):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。换句话说,线程已经做好了执行的就绪准备,表示可以运行了,但还不是正在运行的线程。

  3. 运行状态(Running):当就绪的线程被调度并获得CPU资源时,便进入运行状态,开始执行run()方法的线程执行体。在这个阶段,线程正在执行其任务。

  4. 阻塞状态(Blocked):在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态。阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。阻塞的情况可能包括:

    • 等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。
    • 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。
    • 阻塞于锁:线程试图获取某个锁,但该锁当前被其他线程持有。

    直到线程进入就绪状态,才有机会转到运行状态。

  5. 死亡状态(Dead):当线程退出run()方法时,线程就会自然死亡,处于终止或死亡状态,也就结束了生命周期。

这五个状态构成了线程从创建到消亡的完整生命周期。

三、说一下你对守护线程的了解?

守护线程Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出,因此,在守护线程中执行涉及I/O操作的任务可能会导致数据丢失或其他不可预测的问题。可以通过调用Thread.setDaemon(true)将线程设置为Daemon线程。

👨‍💻面试官追问如何使用守护线程,使用时有什么要注意的

使用方法

  1. 创建线程:首先,你需要创建一个继承自Thread类的新线程或者实现Runnable接口的对象。

  2. 设置守护线程:在调用start()方法之前,通过调用线程的setDaemon(true)方法将其设置为守护线程。

  3. 启动线程:调用线程的start()方法启动线程。

示例

public class DaemonThreadExample extends Thread{public DaemonThreadExample() {// 默认构造函数}@Overridepublic void run() {while (true) {// 守护线程执行的代码System.out.println("守护线程正在运行....");try {Thread.sleep(1000); // 暂停一秒} catch (InterruptedException e) {e.printStackTrace();// 如果守护线程被中断,则退出循环break;}}}public static void main(String[] args) {// 创建守护线程对象DaemonThreadExample daemonThread = new DaemonThreadExample();// 设置为守护线程daemonThread.setDaemon(true);// 启动守护线程daemonThread.start();// 主线程执行其他任务,例如休眠一段时间try {Thread.sleep(5000); // 主线程休眠5秒} catch (InterruptedException e) {e.printStackTrace();}// 当主线程结束时,守护线程也会立即停止System.out.println("当主线程结束时,守护线程停止.");}
}

注意事项

  1. 设置守护线程的时机:必须在调用线程的start()方法之前调用setDaemon(true)方法将其设置为守护线程。如果在调用start()方法之后调用setDaemon(true),则会抛出IllegalThreadStateException

  2. 守护线程与前台线程:守护线程主要是为前台线程服务的。当所有的前台线程都结束时,JVM会立即停止,此时守护线程也会被强制终止。因此,守护线程不应该执行任何重要的或必须完成的任务。

  3. 避免在守护线程中执行I/O操作:由于守护线程的生命周期是不确定的,可能在任何时候被终止,因此在守护线程中执行I/O操作可能会导致数据丢失或文件损坏等问题。

  4. 线程池中的守护线程:如果你在使用线程池(如ExecutorService),并希望线程池中的线程是守护线程,那么你需要确保在调用Executors的工厂方法创建线程池时,传入的线程工厂(ThreadFactory)创建的线程是守护线程。但是,Java的ExecutorService默认并不支持直接设置守护线程,因为线程池通常用于执行重要的后台任务,这些任务应该由前台线程来执行。

  5. 不要依赖守护线程完成关键任务:由于守护线程的生命周期受前台线程的控制,因此不应该依赖守护线程来完成关键任务或需要持久运行的任务。这些任务应该由前台线程来执行。

守护线程在Java编程中有多种应用场景,这些场景通常涉及需要在后台运行的任务,以支持其他线程或执行特定的服务,比如:日志记录、定时任务、数据统计、垃圾回收等。

给大家写一个日志记录的场景:

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class LogRecorder {private final ScheduledExecutorService scheduler;private final BufferedWriter logWriter;public LogRecorder(String logFilePath) throws IOException {// 创建一个单线程的守护线程池scheduler = Executors.newSingleThreadScheduledExecutor(r -> {Thread thread = new Thread(r);// 设置为守护线程thread.setDaemon(true);return thread;});// 初始化日志文件的写入器logWriter = new BufferedWriter(new FileWriter(logFilePath, true));}// 启动日志记录任务public void startLogging() {// 每隔一段时间记录一条日志(这里假设为每5秒)scheduler.scheduleAtFixedRate(() -> {try {// 模拟生成一条日志String logMessage = "日志信息: " + System.currentTimeMillis();logWriter.write(logMessage);logWriter.newLine();logWriter.flush();System.out.println(logMessage);} catch (IOException e) {e.printStackTrace();// 可以在这里处理异常,例如重新打开文件或记录错误日志}}, 0, 5, TimeUnit.SECONDS);}// 停止日志记录任务并关闭文件写入器public void stopLogging() throws IOException {scheduler.shutdown(); // 停止任务调度logWriter.close(); // 关闭文件写入器}public static void main(String[] args) throws IOException {// 假设日志文件路径为"logs/application.log"String logFilePath = "文件地址/xxx.log";LogRecorder logRecorder = new LogRecorder(logFilePath);// 启动日志记录任务logRecorder.startLogging();// 模拟主线程执行一些任务try {Thread.sleep(30000); // 主线程休眠30秒} catch (InterruptedException e) {e.printStackTrace();}// 停止日志记录任务并关闭文件写入器logRecorder.stopLogging();// 主线程结束,由于守护线程的存在,JVM不会立即关闭// 但由于我们调用了scheduler.shutdown(),守护线程中的任务将不再执行System.out.println(" 主线程结束,停止写入日志.");}
}

四、使用多线程可能带来什么问题

在进行并发编程时,如果希望通过多线程执行任务让程序运行得更快,会面临非常多的挑战,比如:

  • 上下文切换的问题:频繁的上下文切换会影响多线程的执行速度。
  • 死锁的问题
  • 受限于硬件和软件的资源限制问题:在进行并发编程时,程序的执行速度受限于计算机的硬件或软件资源。

👨‍💻面试官追问既然你提到了锁,那么死锁产生的必要条件是什么?

  • 互斥条件(Mutual Exclusion):至少有一个资源必须处于非共享状态,即一次只能被一个进程或线程占用。
  • 请求与保持条件(Hold and Wait):进程或线程至少需要持有一个资源,并且在等待其他资源时不释放已占有的资源。
  • 不可剥夺条件(No Preemption):已分配给进程或线程的资源不能被强制性地剥夺,只能由持有资源的进程或线程主动释放。
  • 循环等待条件(Circular Wait):存在一个进程或线程的资源申请序列,使得每个进程或线程都在等待下一个进程或线程所持有的资源。

👨‍💻面试官继续追问那你说说Java多线程避免死锁有什么办法?

  • 按顺序获取锁:当多个线程需要获取多个锁时,为了避免死锁,可以约定一个获取锁的顺序,并且所有线程都按照这个顺序来获取锁。这样可以确保锁是以一致的顺序被请求和释放的。
  • 避免嵌套锁:尽量减少锁的嵌套使用,避免在持有锁的情况下再申请其他锁。如果必须使用多个锁,尽量保证锁的获取顺序一致,以避免死锁。
  • 使用定时锁或tryLock():Java提供了定时锁的机制,即在尝试获取锁的时候设定一个等待的时间。如果在这个时间内未能获取到锁,就主动放弃。另外,可以使用tryLock()方法来尝试获取锁,如果获取失败则不会阻塞,可以继续执行其他逻辑或等待一段时间后重新尝试。
  • 使用并发工具类:Java中的并发工具类,如java.util.concurrent包下的类,提供了许多高级并发工具,如SemaphoreCountDownLatchCyclicBarrier等,这些工具可以帮助简化多线程编程,并减少死锁的风险。
  • 避免线程持有锁的时间过长:当一个线程持有一个锁并长时间不释放时,会阻塞其他线程的访问,并增加死锁的概率。因此,需要尽量缩短线程持有锁的时间,及时释放锁,以便其他线程能够及时获取锁并继续工作。
  • 仔细设计资源申请顺序:在设计多线程程序时,要仔细考虑资源申请的顺序。如果多个线程都需要获取同一组资源,可以考虑引入一个资源分配器,通过分配器来按照一定的策略来分配资源,避免资源的竞争。
  • 死锁检测和恢复:虽然预防死锁是最好的策略,但有时死锁仍然可能发生。在这种情况下,可以使用死锁检测算法来及时发现死锁,并采取必要的措施进行恢复,如终止一个或多个进程或线程,或者回滚到某个一致的状态。

👨‍💻面试官继续追问:你能写一个Java死锁的案例吗?

当两个或多个线程无限期地等待一个资源,而这些资源又被其他线程持有时,就会发生死锁。

public class DeadlockExample {
/*
这个死锁大概思路:
1、线程1拿到lock1休眠5s
2、线程1休眠后,线程2拿到lock2
3、线程1休眠结束后。尝试拿lock2,但是lock2被线程2占有
4、同理,线程2休眠结束后,尝试拿lock1,但是lock1又被线程1占有
因此,造成了死锁
*/public static void main(String[] args) {Object lock1 = new Object();Object lock2 = new Object();new Thread(() -> {synchronized (lock1){System.out.println(Thread.currentThread().getName()+"已经获得a锁");try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"睡眠5ms结束");synchronized (lock2) {System.out.println(Thread.currentThread().getName()+"已经获得b锁");}}},"线程1").start();new Thread(() -> {synchronized (lock2) {System.out.println(Thread.currentThread().getName() + "已经获得b锁");try {Thread.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"睡眠5ms结束");synchronized (lock1) {System.out.println(Thread.currentThread().getName() + "已经获得a锁");}}},"线程2").start();}
}

五、说一说sleep()、wait()、join()、yield()的区别

在说这几个方法区别之前,先给大家说一下什么锁池等待池

1.锁池

所有需要竞争同步锁的线程都会放在锁池中,比如当前对象的锁已经被其中一个线程得到,则其他线程需要在这个锁池进行等待,当前面的线程释放同步锁后锁池中的线程去竞争同步锁,当某个线程得到后会进入就绪队列进行等待cpu资源分配。

2.等待池

当我们调用wait()方法后,线程会放到等待池当中,等待池的线程是不会去竞争同步锁。只有调用notify()notifyAll()后等待池的线程才会开始去竞争锁,notify()是随机从等待池选出一个线程放到锁池,而notifyAll()是将等待池的所以线程放到锁池当中。

sleep跟wait的区别

  1. sleep方法是Thread类的静态方法,wait是Object类的本地方法
  2. sleep方法不会释放锁,但是wait会释放锁,而且会加入到等待队列中

sleep就是把cpu执行资格执行权释放出去,不在运行此线程,当定时时间结束后再取回cpu资源,参与cpu的调度,获取到cpu资源后就可以继续运行了。而如果sleep时该线程有锁,那么sleep也不会释放这个,而是把锁带着进入了冻结状态,也就是说其他需要这个锁的线程根本不可能获取到这把锁。也就是无法执行程序。如果在睡眠期间其他线程调用了这个线程的interrupt方法,那么这个线程也会抛出interruptexception异常返回,这和wait是一样的。

        3.sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。

        4.sleep不需要被唤醒,但是wait需要(不指定时间需要被别人中断)。

        5.sleep一般用于当前线程休眠,或者轮询暂停操作,wait则多用于多线程之间的通信。

        6.sleep会让出CPU执行时间并且强制上下文切换,而wait不一定,wait后还是有机会重新争夺锁继续执行的。

 yield跟join的区别

yield()执行后线程直接进入就绪状态,马上释放cpu的执行权,但是依旧保留了cpu的执行资格,所以有可能cpu下次进行线程调度还会让这个线程获取到执行权继续执行

join()执行后线程进入阻塞状态,例如在线程B中调用线程A的join(),那么线程B会进入到阻塞队列,直到线程A结束或中断线程

给大家举一个简单的例子:t1线程睡4秒,然后执行,之后又调用了join()使主线程进入阻塞,直到t1线程执行完之后主线程才会执行。(注意:是主线程进入阻塞而不是t1阻塞

public static void main(String[] args) throws InterruptedException {Thread t1 =  new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(4000);}catch (InterruptedException e){e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"执行了。。。");}});t1.start();t1.join();System.out.println(Thread.currentThread().getName()+"执行了。。。");}/*打印结果:
Thread-0执行了。。。 (先执行)
main执行了。。。 (后执行)
*/

六、知道线程中的 run() 和 start() 有什么区别吗?

  1. 功能

    • run(): 这是Thread类中的一个方法,用于定义线程要执行的任务。当你直接调用一个线程的run()方法时(例如myThread.run()),它会在当前线程(通常是主线程)中执行,而不是在新的线程中。这意味着它不会启动一个新线程。
    • start(): 这是Thread类中的另一个方法,用于启动一个新线程来执行run()方法中的代码。当你调用start()方法时(例如myThread.start()),Java会创建一个新的线程,并在该线程中调用run()方法。这意味着run()方法中的代码会在新的线程中执行。
  2. 执行上下文

    • 直接调用run():代码在当前线程(通常是主线程)的上下文中执行。
    • 调用start():Java会创建一个新的线程,并在该线程的上下文中执行run()方法中的代码。
  3. 返回值

    • run(): 它没有返回值(即返回类型为void)。
    • start(): 它也没有返回值(返回类型为void),但它启动了一个新线程。
  4. 异常处理

    • 如果你在run()方法中抛出一个未检查的异常(例如RuntimeException),并且你没有在该方法中捕获它,那么它会在当前线程中直接抛出,并且可能会导致程序崩溃(除非有其他地方的代码捕获了该异常)。
    • 如果你在start()方法中抛出一个异常,那么它实际上是在调用start()的线程中抛出的,而不是在新创建的线程中。这是因为start()方法是在当前线程中调用的,而新线程是在start()方法内部创建的。
  5. 线程状态

    • 当线程首次被创建时,它的状态是NEW
    • 当你调用start()方法时,线程的状态变为RUNNABLE(或BLOCKEDWAITINGTIMED_WAITING等,具体取决于线程的行为)。
    • 如果你直接调用run()方法而不是start()方法,线程将不会被创建为单独的线程,并且它的状态仍然是NEW(尽管这在实际中并不常见,因为通常你会在创建线程后立即调用start())。

总结:你应该总是使用start()方法来启动一个新线程,而不是直接调用run()方法。

七、说了这么多,Java程序中如何保证多线程的安全

  • 原子性:在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么都执行,要么都不执行。可以用Java提供了java.util.concurrent.atomic包下的原子类,如AtomicIntegerAtomicLong等,这些类中的方法都是线程安全,或者java.util.concurrent.locks 包下的 Lock 接口提供了比 synchronized 更灵活的锁机制,包括可重入锁、读写锁、定时锁
  • 可见性:Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值

另外,通过synchronizedLock也能够保证可见性,synchronizedLock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

  • 有序性:在Java里面,可以通过volatile关键字来保证一定的“有序性”。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是只有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

总结

这期的面试题需要大家多理解多记多背,先理解在背。好了,今天的分享就到这,喜欢的小伙伴记得三连欧😘

参考文章:并发编程&JVM_ΘLLΘ的博客-CSDN博客

相关文章:

面试题分享之Java并发篇

注意:文章若有错误的地方,欢迎评论区里面指正 🍭 系列文章目录 面试题分享之Java集合篇(三) 面试题分享之Java集合篇(二) 面试题分享之Java基础篇(三) 前言 今天给小…...

bpmn-js 多实例配置MultiInstanceLoopCharacteristics实现或签会签

使用bpmn-js流程图开发过程中会遇到会签和或签的问题,这个时候我们就需要使用多实例配置来实现BPMN 2.0的配置实现了,多实例任务,是从流程编辑概念之初也就是Activiti时期就存在的一个方式。所谓的多实例任务也就是字面意思,一个任务由多个人完成,常见于我们的审批流程的或…...

【gpedit.msc】组策略编辑器的安装,针对windows家庭版,没有此功能

创建一个记事本文件然后放入以下内容 echo offpushd "%~dp0"dir /b %systemroot%\Windows\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum >gp.txtdir /b %systemroot%\servicing\Packages\Microsoft-Windows-GroupPolicy-…...

带EXCEL附件邮件发送相关代码

1.查看生成的邮件 2.1 非面向对象的方式(demo直接copy即可) ​ REPORT Z12. DATA: IT_DOCUMENT_DATA TYPE SODOCCHGI1,IT_CONTENT_TEXT TYPE STANDARD TABLE OF SOLISTI1 WITH HEADER LINE,IT_PACKING_LIST TYPE TABLE OF SOPCKLSTI1 WITH HEADER LIN…...

【算法作业】均分卡牌,购买股票

问题描述 John 有两个孩子,在 John病逝后,留下了一组价值不一定相同的魔卡, 现在要求你设计一种策略,帮John的经管人将John的这些遗产分给他的两个孩子,使得他们获得的遗产差异最小(每张魔卡不能分拆&#…...

python作业

题目 分析 步骤: 判断先画空格还是数字 当有n层时,第i层有多少个空格第i层的起始数字是几,结尾是几,即数字取值范围当有n层时,第i层有多少个数字 代码 模式A n int(input("请输入行数:")) for i in range(…...

【Linux的文件篇章 - 管道文件】

Linux学习笔记---013 Linux的管道文件1、进程间通信1.1、进程为什么要通信?1.2、进程如何通信?1.3、进程通信的方式? 2、匿名管道2.1、理解一种现象2.2、基本概念和管道原理 3、管道的使用3.1、代码样例3.2、如何使用管道通信呢?3…...

C# 局部静态函数,封闭方法中的最佳选择

C# 局部静态函数,封闭方法中的最佳选择 简介特性 应用场景辅助计算递归与尾递归优化筛选与过滤操作查找与映射操作 生命周期静态局部函数 vs 普通局部函数性能封装性可读性 简介 C# 局部静态函数(Local Static Functions)是一种函数作用域内…...

【MySQL】MySQL 8.4.0 长期支持版(LTS)安装

就在2024年 “5.1” 节前,MySQL官方发布了8.4.0长期支持版(LTS - Long Term Support)。根据官方提供的文档,在本地虚拟机进行安装测试。 安装、配置和启动过程记录如下: 第一步,上传到安装包(my…...

nest中的ORM

在 Nest.js 中执行 SQL 查询通常涉及使用 TypeORM 或 Sequelize 这样的 ORM(对象-关系映射)库。这些库使得在 Nest.js 应用程序中连接和操作 SQL 数据库变得更加简单和直观。 以下是一个使用 TypeORM 在 Nest.js 中执行 SQL 查询的示例代码:…...

TCP(Transmission Control Protocol,传输控制协议)如何保证数据的完整性?

TCP(Transmission Control Protocol,传输控制协议)通过一系列机制来保证数据传输的可靠性和无错性,这些机制主要包括: 校验和:TCP报文段包含一个校验和字段,用于检测数据在传输过程中是否出错。…...

Numpy库介绍

NumPy(Numerical Python的缩写)是Python中用于科学计算的一个强大的库。它提供了高性能的多维数组对象(即ndarray)、用于处理这些数组的工具以及用于数学函数操作的函数。让我为你介绍一下它的一些主要功能: 1. 多维数…...

临时有事无法及时签字盖章?试试用契约锁设置“代理人”

遇到“领导休假中、在开重要会议、外出考察或者主任医生手术中等”一段时间内不方便或者无法及时签字盖章的情况怎么办?业务推进不了只能干等? 契约锁电子签及印控平台支持印章、签名“临时授权”、“代理签署”,实现指定人、指定时间段、指定…...

数据库权限管理

1.查看系统级权限(global level) Select * from mysql.user\G; 2.查看数据库中所有表的权限 Select * from mysql.db\G 3.远程连接数据库 第一步在有数据库服务上的主机上:授权 grant all on *.* to root192.168.40.83 identified by Zxy20234; 第…...

如何创建一个 Django 应用并连接到数据库

简介 Django 是一个用 Python 编写的免费开源的 Web 框架。这个工具支持可扩展性、可重用性和快速开发。 在本教程中,您将学习如何为一个博客网站建立与 MySQL 数据库的初始基础。这将涉及使用 django-admin 创建博客 Web 应用程序的骨架结构,创建 MyS…...

【算法刷题day44】Leetcode:518. 零钱兑换 II、377. 组合总和 Ⅳ

文章目录 Leetcode 518. 零钱兑换 II解题思路代码总结 Leetcode 377. 组合总和 Ⅳ解题思路代码总结 草稿图网站 java的Deque Leetcode 518. 零钱兑换 II 题目:518. 零钱兑换 II 解析:代码随想录解析 解题思路 先遍历物品,再遍历背包。 代码…...

『51单片机』AT24C02[IIC总线]

存储器的介绍 ⒈ROM的功能⇢ROM的数据在程序运行的时候是不容改变的,除非你再次烧写程序,他就会改变,就像我们的书本,印上去就改不了了,除非再次印刷,这个就是ROM的原理。 注→在后面发展的ROM是可以可写可…...

Jenkins与Rancher的配合使用

Jenkins和Rancher是两个常用的DevOps工具,可以很好地配合使用来实现持续集成和持续部署。 Jenkins是一个开源的自动化构建工具,可以实现自动化的代码构建、测试和部署等一系列操作。可以通过Jenkins来触发构建任务,例如从代码仓库中拉取最新的…...

GIS入门,常用的多边形平滑曲线算法介绍和JavaScript的多边形平滑曲线算法库chaikin-smooth的实现原理和使用

前言 本章介绍一下常用的多边形平滑曲线算法及其使用案例。 多边形平滑算法通常用于图形处理或计算机图形学中,以使线条或曲线在连接处平滑过渡,而不出现明显的棱角或断裂。多边形平滑算法有多种实现方法,其中一些常见的有下面几种: 贝塞尔曲线插值(Bezier Curve Interpo…...

气膜体育馆内部的采光效果如何?—轻空间

气膜体育馆内部的采光效果如何?这是许多人对这种创新建筑的一个关键关注点。 首先,气膜体育馆的采光性非常好。阳光透过屋顶时以漫射光的方式进入室内,这种透射方式使得室内的光线柔和而均匀。从内部观察,整个屋顶就像一个连续的明…...

矩阵的对称正定性判决(复习)

文章目录 本科学的数学知识忘的太快了 如何判断一个实矩阵是否是对称正定 在线性代数中,一个实对称矩阵是否为正定可以通过以下方法判断: 对称性: 首先,确认矩阵是否对称,即矩阵的转置是否等于其本身。 特征值检查&…...

网络安全之DHCP详解

DHCP:Dynamic Host Configration Protocol 动态主机配置协议 某一协议的数据是基于UDP封装的,当它想确保自己的可靠性时,这个协议要么选确认重传机制,要么选周期性传输。 DHCP是确认重传,【UDP|DHCP】,当DHCP分配完地…...

【Proteus】LED呼吸灯 直流电机调速

1.LED呼吸灯 #include <REGX51.H> sbit LEDP2^0; void delay(unsigned int t) {while(t--); } void main() {unsigned char time,i;while(1){for(time0;time<100;time){for(i0;i<20;i){LED0;delay(time);LED1;delay(100-time);}}for(time100;time>0;time--){fo…...

今天遇到一个GPT解决不了的问题

问题描述 你好&#xff0c;postman的一个post请求&#xff0c;编辑器里面放了一个很长的json数据&#xff0c;报Tokenization is skipped for long lines for performance reasons. This can be configured via editor.maxTokenizationLineLength.&#xff0c;但是同样的数据&a…...

优化SQL的方法

来自组内分享&#xff0c;包含了比较常使用到的八点&#xff1a; 避免使用select * union all代替union 小表驱动大表 批量操作 善用limit 高效的分页 用连接查询代替子查询 控制索引数量 一、避免使用select * 消耗数据库资源 消耗更多的数据库服务器内存、CPU等资源。 消…...

库存管理系统开源啦

软件介绍 ModernWMS是一个针对小型物流仓储供应链流程的开源库存管理系统。该系统的开发初衷是为了满足中小型企业在有限IT预算下对仓储管理的需求。通过总结多年ERP系统研发经验&#xff0c;项目团队开发了这套适用于中小型企业的系统&#xff0c;以帮助那些有特定需求的用户。…...

【java】接口

什么是接口 接口当中存在的是对方法的定义&#xff0c;而不是对方法的具体实现。 为什么不实现这个方法呢&#xff1f; 继承的本质是代码的复用。当一个父类会经常被继承&#xff0c;并且子类都要自己实现方法时&#xff0c;父类中的方法就会显得累赘&#xff0c;并且占用了…...

Java中的类型转换

一、类型转换 对类型转换来说分为向上类型转换和向下类型转换&#xff1a; 向上类型转换是自动完成的&#xff0c;一般是小类型向大类型转换。在引用类型中是子类型向父类型转换。向下类型转换是强制完成的&#xff0c;一般是大类型向小类型转换。在引用类型中是父类型向子类…...

定义范围对PFMEA分析的重要性——SunFMEA软件

在进行PFMEA分析时&#xff0c;定义范围是一个至关重要的步骤。这是因为&#xff0c;通过明确分析的范围&#xff0c;可以确保团队关注到最关键、最可能影响产品质量的过程&#xff0c;从而更有效地识别和解决潜在问题。今天SunFMEA软件和大家一起讨论定义范围对PFMEA操作的重要…...

json返回工具类|世界协调时间(UTC)

一、问题 世界协调时间&#xff08;UTC&#xff09;是一个标准的时间参考&#xff0c;通常被用于跨越不同时区的时间标准。要将 UTC 时间转换为中国时间&#xff08;中国标准时间&#xff09;&#xff0c;你需要将时间加上8个小时&#xff0c;因为中国位于 UTC8 时区。 初中知…...

MySQL·内置函数

目录 函数 日期函数 案例1&#xff1a;创建一张表&#xff0c;记录生日 案例2&#xff1a;创建一个留言表 案例3&#xff1a;请查询在2分钟内发布的帖子 字符串函数 案例1&#xff1a; 获取emp表的ename列的字符集 案例2&#xff1a;要求显示exam_result表中的信息&am…...

vue根据文字动态判断溢出...鼠标悬停显示el-tooltip展示

使用自定义el- tooltip 组件 定义 Tooltip是一种小型弹出框,它显示有关特定页面元素的信息,例如按钮、链接或图标。Tooltip通常以半透明的气泡形式呈现,并出现在页面元素的旁边或下方。 它可以改善用户体验,使用户更容易理解页面元素的功能和意图。用户可以通过将鼠标悬停…...

使用Tkinter实现数据预测工具的GUI界面展示

如果构建好预测模型后&#xff0c;想将预测模型通过一个交互式的页面显示&#xff0c;可以通过下边两种方式实现。 本文中代码有详细解析注释&#xff0c;便不再如往期一样分开讲解了&#xff0c;有需要的朋友可以直接拿去使用&#xff0c;代码可以直接运行&#xff0c;把预测…...

机器学习笔记-22

终章 至此吴恩达老师的机器学习课程已经完成啦&#xff0c;总结一下&#xff1a; 1.监督学习的算法&#xff1a;线性回归、逻辑回归、神经网络和向量机 2.无监督学习的算法&#xff1a;K-Means、PCA、异常检测 3.推荐系统、大规模数据处理、正则化、如何评估算法 4.上限分析、…...

车间为什么选择蒸发式冷风机?

蒸发式冷风机具有以下特点&#xff1a; 节能环保&#xff1a;蒸发式冷风机不使用压缩机和化学制冷剂&#xff0c;而是通过水的蒸发来降低温度&#xff0c;因此它是无压缩机、无冷媒、无污染的环保型产品。降温效果显著&#xff1a;在较潮湿地区&#xff0c;它一般能达到5-9℃的…...

5分钟速通大语言模型(LLM)的发展与基础知识

✍️ 作者&#xff1a;哈哥撩编程&#xff08;视频号同名&#xff09; 博客专家全国博客之星第四名超级个体COC上海社区主理人特约讲师谷歌亚马逊演讲嘉宾科技博主极星会首批签约作者 &#x1f3c6; 推荐专栏&#xff1a; &#x1f3c5; 程序员&#xff1a;职场关键角色通识宝…...

vue项目开发流程

vue项目开发流程 环境配置 asdf plugin add nodejs asdf install nodejs 16.20.2创建项目 npm create vitelatest my-vue-app -- --template vue npm install npm run dev修改调试端口 修改vite.config.js,修改如下所示&#xff0c;添加server的host和port。 import { de…...

【Django学习笔记(十)】Django的创建与运行

Django的创建与运行 前言正文1、安装Django2、创建项目2.1 基于终端创建项目2.2 基于Pycharm创建项目2.3 两种方式对比 3、默认项目文件介绍4、APP5、启动运行Django5.1 激活App5.2 编写URL和视图函数对应关系5.3 启动Django项目5.3.1 命令行启动5.3.2 Pycharm启动5.3.3 views.…...

即时通讯技术文集(第37期):IM代码入门实践(Part1) [共16篇]

为了更好地分类阅读 52im.net 总计1000多篇精编文章&#xff0c;我将在每周三推送新的一期技术文集&#xff0c;本次是第37 期。 [- 1 -] 一种Android端IM智能心跳算法的设计与实现探讨&#xff08;含样例代码&#xff09; [链接] http://www.52im.net/thread-783-1-1.html […...

UV胶具有哪些特点和优势

1. 快速固化&#xff1a;UV胶在紫外线照射下能够迅速固化&#xff0c;固化时间通常在几秒钟到几分钟之间&#xff0c;大大提高了生产效率。 2. 高粘接强度&#xff1a;UV胶固化后&#xff0c;具有较高的粘接强度&#xff0c;能够在各种材料上实现可靠的粘接&#xff0c;提供持…...

python面试之mysql引擎选择问题

MySQL数据库提供了多种存储引擎&#xff0c;每种存储引擎有其特定的优势和场景适用。以下是几种常见的MySQL存储引擎及其特点&#xff1a; InnoDB&#xff1a; 支持事务&#xff0c;有回滚和提交事务的功能。 支持行级锁定&#xff0c;提供更高的并发。 支持外键约束&#…...

MT3031 AK IOI

思路&#xff1a;把每个节点存到堆&#xff08;大根堆&#xff09;里。 如果节点放入后总时间没有超过m则放入堆中&#xff1b;如果总时间超过了&#xff0c;就看堆头元素是否比新元素大。如果大&#xff0c;则删除堆头&#xff08;反悔贪心&#xff09;。 注意别忘记开long l…...

UE5自动生成地形二:自动生成插件

UE5自动生成地形二&#xff1a;自动生成插件 Polycam使用步骤 本篇主要讲解UE5的一些自动生成地形的插件 Polycam 此插件是通过现实的多角度照片自动建模生成地形数据&#xff0c;也是免费的。这里感谢B站up主古道兮峰的分享 Polycam网站 插件下载地址 插件网盘下载 提取码&a…...

二分图(染色法与匈牙利算法)

二分图当且仅当一个图中不含奇数环 1.染色法 简单来说&#xff0c;将顶点分成两类&#xff0c;边只存在于不同类顶点之间&#xff0c;同类顶点之间没有边。 e.g. 如果判断一个图是不是二分图&#xff1f; 开始对任意一未染色的顶点染色。 判断其相邻的顶点中&#xff0c;若未…...

ReactFlow的ReactFlow实例事件传参undefined处理状态切换

1.问题 ReactFlow的ReactFlow实例有些事件我们在不同的状态下并不需要&#xff0c;而且有时候传参会出现其它渲染效果&#xff0c;比如只读状态下我们不想要拖拉拽onEdgesChange连线重连或删除的功能。 2.思路 事件名称类型默认值onEdgesChange(changes: EdgeChange[]) >…...

Dockerfile 和 Docker Compose

Dockerfile 和 Docker Compose 是 Docker 生态系统中两个重要的组成部分&#xff0c;它们分别服务于不同的目的&#xff0c;但共同协助开发者和运维人员高效地管理和部署容器化应用。 Dockerfile Dockerfile 是一个文本文件&#xff0c;包含了构建 Docker 镜像所需的一系列指…...

多个文件 import 的相同模块里的对象

多个文件 import 的相同模块里的对象&#xff0c;是否永远都是同一个对象&#xff1f; 在store的index.js中 import vue from ‘vue’ import Vuex from ‘vuex’ 并配置有关对象 然后再app.vue中配置vm 在不同的文件中 import一个vue对象&#xff0c;在任何情况下&#…...

面试经典150题——验证回文串

面试经典150题 day25 题目来源我的题解方法一 双指针方法二 双指针 空间优化 题目来源 力扣每日一题&#xff1b;题序&#xff1a;125 我的题解 方法一 双指针 首先去除掉字符串中的无用字符&#xff0c;并将英文字符转换为小写&#xff0c;然后使用双指针来判断是否是回文串…...

YOLOv8的训练、验证、预测及导出[目标检测实践篇]

这一部分内容主要介绍如何使用YOLOv8训练自己的数据集&#xff0c;并进行验证、预测及导出&#xff0c;采用代码和指令的两种方式&#xff0c;参考自官方文档&#xff1a;Detect - Ultralytics YOLOv8 Docs。实践篇不需要关注原理&#xff0c;只需要把流程跑通就行&#xff0c;…...

光伏远动通讯屏的组成

光伏远动通讯屏的组成 远动通讯屏主要用于电力系统数据采集与转发&#xff0c;远动通讯屏能够采集站内的各种数据&#xff0c;如模拟量、开关量和数字量等&#xff0c;并通过远动通讯规约将必要的数据上传至集控站或调度系统。这包括但不限于主变和输电线路的功率、电流、电压等…...

前端面试题(三)答案版

面试形式&#xff1a;线上面试&#xff08;不露脸&#xff1a;腾讯会议&#xff09;&#xff1a;时长30分钟 面试评价&#xff1a;面试官给的题目宽泛&#xff0c;面试者灵活作答 面试官&#xff1a;技术人员 面试官的提问大纲&#xff1a;我们自身简历本公司招聘要求 面试…...

c# 多线程同时只读不写DataTable的数据有没有可能出错,如果多线程并发读修改DataTable,如何处理,多线程同时修改DataTable数据

在C#中&#xff0c;如果多个线程同时执行只读操作&#xff08;即不修改DataTable的内容&#xff09;&#xff0c;从理论上讲&#xff0c;应该是安全的。因为只读操作不会改变DataTable的状态&#xff0c;所以不会出现数据竞争或一致性问题。然而&#xff0c;实际情况可能会受到…...

selenium发展史

Selenium Core 2004 年&#xff0c;Thoughtworks 的工程师 Jason Huggins 正在负责一个 Web 应用的测试工作&#xff0c;由于这个项目需要频繁回归&#xff0c;这导致他不得不每天做着重复且低效的工作。为了解决这个困境&#xff0c;Jason 开发了一个运行在 JavaScript 沙箱中…...

从理论到实践:Copula在量化分析中的应用

查看原文>>>从理论到实践&#xff1a;Copula在量化分析中的应用 - 知乎 在工程、水文和金融等各学科的研究中&#xff0c;总是会遇到很多变量&#xff0c;研究这些相互纠缠的变量间的相关关系是各学科的研究的重点。虽然皮尔逊相关、秩相关等相关系数提供了变量间相关…...

尝试使用官方jailhouse-images仓库运行jailhouse

jailhouse 的官方 demo 演示仓库 Demo and testing images: https://github.com/siemens/jailhouse-images 通过jailhouse 的官方 demo 演示仓库&#xff0c;可以直接编译出带有部署有jailhouse程序的Linux镜像&#xff0c;有多个目标平台的Linux镜像可选&#xff0c;也有在qe…...

初步揭开缓存神秘面纱之双map实现缓存管理的类

在应用程序中&#xff0c;缓存是一种常见的优化手段&#xff0c;可以提高数据的访问速度。针对缓存管理&#xff0c;我们通常会实现一些类来方便地管理缓存数据。缓存具体是如何实现的&#xff0c;这里我们利用双map做一个缓存的基本实现。 1.考虑缓存有哪些属性 1.是否是永久…...