核心三:正确的停止线程

3、核心三:正确的停止线程

3.1 如何正确的停止线程

3.1.1 原理介绍:使用interrupt来通知,而不是强制

  • 线程中断的机制:一个线程来通知要中断线程(你好,你现在应该停止了)
  • 最后的决定是由要中断程序来执行。如果要中断程序不想停止,那么我们也无能无力
  • 这是合理的,why,我们停止一个线程需要很多步骤(比如,存储停止前线程的状态等)这些步骤由当前线程自己来执行比较好(自己很清楚自己的情况)

3.1.2 通常线程会在什么情况下停止

  1. run方法全部执行完毕(最常见)
  2. 存在异常,方法也没有捕获,线程会停止

3.1.3 正确的停止方法:interrupt(这里会给出不同情况我们应该如何停止线程)

情况1:通常线程会在什么情况下停止

/*** run方法没有sleep或wait方法时,停止线程*/
public class RightWayStopThreadWithoutSleep implements Runnable{@Overridepublic void run () {int num = 0;while(num <= Integer.MAX_VALUE/2){if(num % 10000 == 0){System.out.println(num+" 是10000的倍数");}num++;}System.out.println("任务运行结束了");}public static void main (String[] args) throws InterruptedException {Thread thread = new Thread(new RightWayStopThreadWithoutSleep());thread.start();Thread.sleep(1000);thread.interrupt();}
}
//这种情况下,线程被中断后,run方法还是会执行完毕
//因为:方法中没有能够响应中断的机制(注意:线程早就被停止了,只不过run方法还在执行)

正确停止线程的方法

/*** run方法没有sleep或wait方法时,停止线程*/
public class RightWayStopThreadWithoutSleep implements Runnable{@Overridepublic void run () {int num = 0;while(!Thread.currentThread().isInterrupted() && num<=Integer.MAX_VALUE/2){if(num % 10000 == 0){System.out.println(num+" 是10000的倍数");}num++;}System.out.println("任务运行结束了");}public static void main (String[] args) throws InterruptedException {Thread thread = new Thread(new RightWayStopThreadWithoutSleep());thread.start();Thread.sleep(1000);thread.interrupt();}
}
//这种情况下我们在循环里面加入了判断条件 !Thread.currentThread().isInterrupted()
//此时,run方法能被停止

情况2:线程可能被阻塞

/*** 带有sleep的中断线程的用法(该段代码正确的停止线程)* sleep方法能够响应中断机制*/
public class RightWayStopThreadWithSleep {public static void main (String[] args) throws InterruptedException {Runnable runnable = ()->{int num = 0;try {while(num<=300 && !Thread.currentThread().isInterrupted()){if(num % 100 == 0){System.out.println(num+"是100的倍数");}num++;}Thread.sleep(1000);//k} catch (Exception e) {e.printStackTrace();System.out.println(Thread.currentThread().isInterrupted());}};Thread thread = new Thread(runnable);thread.start();Thread.sleep(500);//500的设计是为了实现:中断线程时,我们正在执行kthread.interrupt();}
}

输出

在这里插入图片描述

看到没有,在调用thread.interrupt()语句时,我们正处于run方法中的Thread.sleep(1000)。此时线程正在被sleep方法阻塞,我们停止线程成功。为什么是成功了呢?明明最后输出false?这个原因看到后面(情况四)就明白了

情况3:如果线程在每次迭代(循环)后都阻塞

/*** 每次循环中都有sleep或wait方法,那么不需要每次迭代都检查是否中断(该段代码正确的停止线程)*  !Thread.currentThread().isInterrupted()*/
public class RightWayStopThreadWithSleepEveryLoop {public static void main (String[] args) throws InterruptedException {Runnable runnable = ()->{int num = 0;try {while(num<=10000){if(num % 100 == 0){System.out.println(num+"是100的倍数");}num++;Thread.sleep(10);}} catch (Exception e) {e.printStackTrace();System.out.println(Thread.currentThread().isInterrupted());}};Thread thread = new Thread(runnable);thread.start();Thread.sleep(5000);thread.interrupt();}
}

输出

在这里插入图片描述

我们成功停止了线程。报错原因和上面一样

情况4:while内try/catch的问题

/*** 如果while里面放try/catch,会导致中断失效*/
public class CantInterrupt {public static void main (String[] args) throws InterruptedException {Runnable runnable = ()->{int num = 0;while(num<=10000&&!Thread.currentThread().isInterrupted()){if(num % 100 == 0){System.out.println(num+"是100的倍数");}num++;try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}};Thread thread = new Thread(runnable);thread.start();Thread.sleep(5000);thread.interrupt();}
}

输出

在这里插入图片描述

我们停止线程失败了。因为Java语言在设计sleep函数的时候,有这样的一个理念。就是,当他一旦响应中断,便会把线程的interrupt标记位给清除。也就是说,在刚刚的情况下,它确实在sleep过程中收到了中断,而且也catch并打印出异常信息。但是,由于sleep设计的理念,导致interrupt标记位被清除。所以我们每次循环判断Thread.currentThread().isInterrupted()都会返回false。还有看看try_catch放在循环里面,catch打印异常信息之后,循环继续😂

正确的停止这种情况下的线程方法在后面

情况5:实际开发中的两种最佳实践💟

优先选择:传递中断 1

不想或无法传递:恢复中断 2

不应屏蔽中断(吞了中断信息,例如第一种实践中的try语句),这种情况非常不可取

  1. 第一种最佳实践(优先选择:传递中断)

    这里的传递就是指异常的传递,即throws异常

    /*** 最佳实践(最好的停止线程方法):catch了InterruptedExcetion之后的优先选择:* 在方法签名中抛出异常* 那么在run方法中就会强制要求try/catch* 下面的代码是错误的示范*/
    public class RightWayStopThreadInProd implements Runnable{@Overridepublic void run () {while(true){System.out.println("go");throwInMethod();}}private void throwInMethod(){try {Thread.sleep(2000);//k} catch (InterruptedException e) {//在执行k时,我们收到中断信号。此时我们代码在此处进行了消极处理://try/catch将中断信号吞了(没有上报领导run,导致领导没有收到中断通知,中断失败)e.printStackTrace();}}public static void main (String[] args) throws InterruptedException {Thread thread = new Thread(new RightWayStopThreadInProd());thread.start();Thread.sleep(1000);thread.interrupt();}
    }
    

    输出

    在这里插入图片描述

    我们中断成功了。只不过run还没有停止执行。判断Thread.currentThread().isInterrupted()都会返回false。

    处理方法:try/catch改为throws

    public class RightWayStopThreadInProd implements Runnable{@Overridepublic void run () {while(true && !Thread.currentThread().isInterrupted()){System.out.println("go");try {throwInMethod();} catch (InterruptedException e) {e.printStackTrace();}}}private void throwInMethod() throws InterruptedException {Thread.sleep(2000);}public static void main (String[] args) throws InterruptedException {Thread thread = new Thread(new RightWayStopThreadInProd());thread.start();Thread.sleep(1000);thread.interrupt();}
    }
    

    输出

    在这里插入图片描述

    看到了没,我们可以正确处理响应中断请求。我们中断成功了。只不过run还没有停止执行。判断Thread.currentThread().isInterrupted()都会返回false。

  2. 第二种最佳实践(不想或无法传递:恢复中断)

    /*** 最佳实践2:在catch语句中调用Thread.currentThread().interrupt()来恢复中断状态* 以便于在后续的执行中,依然能够检查到刚才发生了中断* 回到刚才RightWayStopThreadInProd补上中断,让他跳出run*/
    public class RightWayStopThreadInProd2 implements Runnable{@Overridepublic void run () {while(true){System.out.println("1");if(Thread.currentThread().isInterrupted()) {System.out.println("程序运行结束");break;}reInterrupt();}}private void reInterrupt() {try {Thread.sleep(3000);} catch (InterruptedException e) {Thread.currentThread().interrupt();//这条语句解决了try语句独吞中断信号e.printStackTrace();}}public static void main (String[] args) throws InterruptedException {Thread thread = new Thread(new RightWayStopThreadInProd2());thread.start();Thread.sleep(2000);thread.interrupt();}
    }
    

    输出

    在这里插入图片描述

    我们正确的停止了线程,并且停止了run方法的继续执行

    现在为了显示Thread.currentThread().interrupt();这条语句的重要性。我们注释掉他,结束输出:

    在这里插入图片描述

    这条语句是解决try语句独吞中断信号的情况,导致run知道中断信息,以便于停止run方法

情况6:响应中断的方法总结列表

响应中断表示:当我们执行wait()方法时,如果有中断信号过来了我们可以知道中断过来了

例如 Thread.sleepObject.wait 等,都会检查线程何时中断,并且在发现中断时提前返回。他们在响应中断时执行的操作包括:清除中断状态,抛出 InterruptedException ,表示阻塞操作由于中断而提前结束。
在这里插入图片描述

在这里插入图片描述

3.1.4 正确停止带来的好处(使用interrupt来中断的好处)

被中断的线程拥有控制是否中断的权限。这一点非常重要,被中断的线程自己最了解自己,数据的备份、线程状态的存储自己亲自来进行比较合理。这样的做法我们保证了线程的安全和完整。

3.2 interrupt()源码分析💟

再次强调,该方法只是通知一下(设置线程的中断标志为true),不是强制执行

public class Thread implements Runnable {public void interrupt() {if (this != Thread.currentThread()) {checkAccess();// thread may be blocked in an I/O operationsynchronized (blockerLock) {Interruptible b = blocker;if (b != null) {interrupted = true;interrupt0();  // inform VM of interruptb.interrupt(this);return;}}}interrupted = true;// inform VM of interruptinterrupt0();}//关键就在这里,但这是C++的代码。老师在彩蛋里面有明确的说明:如何查看native代码private native void interrupt0();
}

判断是否已被中断相关方法

public static boolean interrupted() 1

public boolean isInterrupted() 2

  • 作用:检测当前线程是否被中断
  • 方法1,在返回结果时,直接清除线程的中断状态(注意:一个线程被清除中断状态后再次调用方法判断是否中断时,返回false(除非当前线程在第一次调用清除其中断状态之后和第二次调用检查它之前再次中断)
  • 方法2,返回目标线程的中断状态
  • 方法1,他的目标对象应该是当前线程(执行这条语句的线程)。而不是方法调用者

源码:

  1. static boolean interrupted()

    /**
    * 测试当前线程是否已中断。此方法清除线程的 中断状态 。换言之,如果连续调用此方法两次,则第二次调* 用将返回 false(除非当前线程在第一次调用清除其中断状态之后和第二次调用检查它之前再次中断)。
    * 返回:true 如果当前线程已中断; false 否则。
    */
    public static boolean interrupted() {//true,说明清除中断标志return currentThread().isInterrupted(true);
    }
    
  2. boolean isInterrupted()

    public boolean isInterrupted() {//false,说明不清除中断标志return isInterrupted(false);
    }
    

练习

/*** 注意Thread.interrupted()方法的目标对象是“当前线程 ”* 而不管本方法是由谁来调用的*/
public class RightWayInterrupted {public static void main (String[] args) {Thread thread = new Thread(new Runnable() {@Overridepublic void run () {}});thread.start();thread.interrupt();System.out.println(thread.isInterrupted());System.out.println(thread.interrupted());//此方法,处理对象是执行这条语句的线程mainSystem.out.println(Thread.interrupted());//此方法,处理对象是执行这条语句的线程mainSystem.out.println(thread.isInterrupted());}
}

输出

true
false
false
true

3.3 错误的停止方法(两种)

被弃用的stop,suspend和resume方法

/*** 错误的停止线程:用stop()来停止线程,会导致线程运行一半时突然停止,没办法完成一个基本单位的操作* 会造成脏数据*/
public class StopThread implements Runnable{@Overridepublic void run () {//模拟指挥军队:一共5个联队,队有10人。以连队为单位,发送弹药for (int i = 0; i < 5; i++) {System.out.println("联队"+i+"开始领取武器");for (int j = 0; j < 10; j++) {System.out.println(j);try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("联队"+i+"领取武器了");}}public static void main (String[] args) {Thread thread = new Thread(new StopThread());thread.start();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}thread.stop();//此时停止线程会发生错误}
}

输出

联队0开始领取武器
0
1
2
3
4
5
6
7
8
9
联队0领取武器了
联队1开始领取武器
0
1
2
3
4
5

我们发放弹药是以连队为单位的,错误的停止线程,使联队1中有人领取了,有人没有领取。这样的结果是大忌。千万要避免

注意事项:

  1. 关于stop理论错误的说法:不使用stop方法,是因为该方法不会释放所有的监视器
  2. suspend不会释放锁,可能会造成死锁

用volatile设置boolean标记位💟

/*** 演示volatile的局限* part1:看似可行*/
public class WrongWayVolatile implements Runnable{private volatile boolean canceled = false;@Overridepublic void run () {int num = 0;try {while (num<=100000 && !canceled){if(num % 100 == 0){System.out.println(num+"是100的倍数");}num++;Thread.sleep(1);}} catch (InterruptedException e) {e.printStackTrace();}}public static void main (String[] args) throws InterruptedException {WrongWayVolatile aVolatile = new WrongWayVolatile();Thread thread = new Thread(aVolatile);thread.start();Thread.sleep(5000);aVolatile.canceled = true;}
}

输出:

在这里插入图片描述

在这种情况下,我们确实实现了线程的终止

但是,还有一种情况,我们没有实现停止线程的任务。在《并发编程实战》中对这种情况描述为:

如果使用这种方法(用volatile设置boolean标记位)的任务调用了一个阻塞方法,例如 BlockingQueue.put,那么可能会产生一个更严重的问题——任务可能永远不会检查取消标志,因此永远不会结束

/*** 演示用volatile的局限性:陷入阻塞时,volatile是无法停止线程的* 注意:此例中,生产者的生产速度很快,消费者的消费速度慢,所以阻塞队列满了以后* 生产者就会阻塞,等待消费者进一步消费*/
public class WrongWayVolatileCantStop {public static void main (String[] args) throws InterruptedException {ArrayBlockingQueue storage = new ArrayBlockingQueue(10);Producer producer = new Producer(storage);Thread producerThread = new Thread(producer);producerThread.start();Thread.sleep(10);Consumer consumer = new Consumer(storage);while (consumer.needMoreNums()){System.out.println(consumer.storage.take()+"被消费了");Thread.sleep(100);}System.out.println("消费者不需要跟多数据了");//一旦消费者不需要更多数据了,应该让生产者停止,否则就浪费了producer.canceled = true;System.out.println(producer.canceled);}
}
class Producer implements Runnable{BlockingQueue storage;//存放生产物品的仓库public volatile boolean canceled = false;public Producer (BlockingQueue storage) {this.storage = storage;}@Overridepublic void run () {int num = 0;try {while (num<=100000 && !canceled){if(num % 100 == 0){storage.put(num);System.out.println(num+"是100的倍数,它已经被放到仓库中");}num++;}} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println("生产者停止运行");//这句没有执行;表示线程没有真正的停止}}
}class Consumer{BlockingQueue storage;//从该仓库中取出public Consumer (BlockingQueue storage) {this.storage = storage;}public boolean needMoreNums(){if (Math.random()>0.95){return false;}return true;}
}

结果

在这里插入图片描述

原因 :

一旦,队列满了,put方法也会被阻塞。当生产者在put方法中阻塞时,如果消费者希望取消生产者任务,那消费者使用producer.canceled = true;。那么就会发生上面的结果。因为生产者无法从阻塞的put方法中恢复过来(因为队列满了并且消费者停止了)

一旦,仓库满了storage.put(num);就不会执行下面的所有代码。因此,我们线程停止失败了

while (num<=100000 && !canceled){if(num % 100 == 0){storage.put(num);System.out.println(num+"是100的倍数,它已经被放到仓库中");}num++;
}

我们看看这个方法的具体实现

在这里插入图片描述

对于上面的生产者消费者模型,我们使用正确的停止线程的方式来终止线程

/*** 用中断来修复刚才的无尽等待问题*/
public class WrongWayVolatileFixed {public static void main (String[] args) throws InterruptedException {WrongWayVolatileFixed body = new WrongWayVolatileFixed();ArrayBlockingQueue storage = new ArrayBlockingQueue(10);Producer producer = body.new Producer(storage);Thread producerThread = new Thread(producer);producerThread.start();Thread.sleep(1000);Consumer consumer = body.new Consumer(storage);while (consumer.needMoreNums()) {System.out.println(consumer.storage.take() + "被消费了");Thread.sleep(100);}System.out.println("消费者不需要跟多数据了");//一旦消费者不需要更多数据了,应该让生产者停止,否则就浪费了producerThread.interrupt();}class Producer implements Runnable {BlockingQueue storage;//存放生产物品的仓库public Producer (BlockingQueue storage) {this.storage = storage;}@Overridepublic void run () {int num = 0;try {while (num <= 100000 && !Thread.currentThread().isInterrupted()) {if (num % 100 == 0) {storage.put(num);System.out.println(num + "是100的倍数,它已经被放到仓库中");}num++;}} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println("生产者停止运行");}}}class Consumer {BlockingQueue storage;//从该仓库中取出public Consumer (BlockingQueue storage) {this.storage = storage;}public boolean needMoreNums () {if (Math.random() > 0.95) {return false;}return true;}}
}

输出

在这里插入图片描述

3.4 常见面试题

如何停止线程

  1. 使用interrupt来请求中断,好处:保证数据安全,主动权在被中断的线程手上
  2. 要想成功中断,需要三方相互配合:请求方、被停止方、子方法被调用方
  3. 最后再说错误中断的方法:stop/suspend已弃用,volatile的boolean无法处理长时间阻塞的情况

如何处理不可中断的阻塞

  1. 如果线程阻塞是由于调用了 wait(),sleep() 或 join() 方法,你可以中断线程,通过抛出 InterruptedException 异常来唤醒该线程。但是对于不能响应InterruptedException的阻塞,很遗憾,并没有一个通用的解决方案。
  2. 但是我们可以利用特定的其它的可以响应中断的方法,比如ReentrantLock.lockInterruptibly(),比如关闭套接字使线程立即返回等方法来达到目的。
  3. 总结就是说如果不支持响应中断,就要用特定方法来唤起,没有万能药。

3.5 关于run方法不能抛出异常的讨论

看代码

在这里插入图片描述

此时IDEA报错

‘run()’ in ‘Anonymous class derived from java.lang.Runnable’ clashes with ‘run()’ in ‘java.lang.Runnable’; overridden method does not throw ‘java.lang.Exception’

我们只能try

在这里插入图片描述

原因,请看下面的源码

@FunctionalInterface
public interface Runnable {/*** When an object implementing interface {@code Runnable} is used* to create a thread, starting the thread causes the object's* {@code run} method to be called in that separately executing* thread.* <p>* The general contract of the method {@code run} is that it may* take any action whatsoever.** @see     java.lang.Thread#run()*/public abstract void run();
}

如有不对的地方请在评论区指正

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

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

相关文章

第十四届蓝桥杯c++研究生组

A 关键思路是求每个十进制数的数字以及怎么在一个数组中让判断所有的数字次数相等。 求每个十进制的数字 while(n!0){int x n%10;//x获取了n的每一个位数字n/10;}扩展&#xff1a;求二进制的每位数字 &#xff08;注意&#xff1a;进制转换、1的个数、位运算&#xff09; x…

主干网络篇 | YOLOv8更换主干网络之MobileNeXt | 新一代移动端模型MobileNeXt来了!

前言:Hello大家好,我是小哥谈。MobileNeXt是由微软研究院提出的一种高效的卷积神经网络结构,它在保持模型轻量级的同时,能够获得较高的性能。MobileNeXt采用了一种称为Inverted Residuals with Linear Bottlenecks(IRL)的结构,通过深度可分离卷积和快捷连接来减少模型的…

[Android]将私钥(.pk8)和公钥证书(.pem/.crt)合并成一个PKCS#12格式的密钥库文件

如下&#xff0c;我们有一个platform.pk8和platform.x509.pem。为了打包&#xff0c;需要将私钥&#xff08;.pk8&#xff09;和公钥证书&#xff08;可能是.pem或.crt文件&#xff09;合并成一个PKCS#12 格式的密钥库文件 1.准备你的私钥和证书文件 确保你有以下两个文件&…

设计模型-系统架构师(三)

1、按照《中华人民共和国著作权法》的权利保护期&#xff0c;署名权和修改权 是永久保护的。 发表权、使用权和获取报酬权 保护期限 作者终生和死后的50年。 2、&#xff08;重要&#xff09;面向对象的分析模型主要由 顶层架构图、用例与用例图和&#xff08;&#xff09;构成…

2024年物理化学与工程技术发展国际会议(ICPCETD 2024)

全称&#xff1a;2024年物理化学与工程技术发展国际会议&#xff08;ICPCETD 2024&#xff09; 会议网址:http://www.icpcetd.com 会议时间: 2024.06.30 截稿时间&#xff1a;2024.06.21 会议地点: 上海 投稿邮箱&#xff1a;icpcetd_info163.com 投稿标题&#xff1a;ArticleT…

前端开发框架Angular

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Angular概述 Angular是由Google开发并维护的一款开源前端开发框架。它最初被设计为用于构建单页面应用&#xff08;SPA&#xff09;&#xff0c;但随着版本的更新和发展&am…

刚接触抖店并开通了个体店,怎么快速起店呢?运营思路参考如下

我是王路飞。 如果你刚接触抖店&#xff0c;并且开通了个体店&#xff0c;但不知道如何做店的话。 其实很简单&#xff0c;抖店的流程并没有这么复杂。 电商的核心无非就是产品&#xff0c;抖店的运营也都是围绕产品展开的。 我给你们说下抖店的运营思路你们可以作为参考&a…

【Real】[Flask]SSTI

文章目录 前言一、题目解读二、解题过程三、知识点Flask是什么SSTI是什么SSTI是如何形成的易于利用的类payload是什么 探索类型和类层次结构和方法 前言 温馨提示&#xff1a;看到哪里不懂直接跳到知识点部分&#xff0c;理解完再回到解题过程。 一、题目解读 题目是[Flask]S…

如何使用Docker快速运行Firefox并实现远程访问本地火狐浏览器

文章目录 1. 部署Firefox2. 本地访问Firefox3. Linux安装Cpolar4. 配置Firefox公网地址5. 远程访问Firefox6. 固定Firefox公网地址7. 固定地址访问Firefox Firefox是一款免费开源的网页浏览器&#xff0c;由Mozilla基金会开发和维护。它是第一个成功挑战微软Internet Explorer浏…

【Linux学习】进程间通信 (3) —— System V (1)

下面是有关进程通信中 System V 的相关介绍&#xff0c;希望对你有所帮助&#xff01; 小海编程心语录-CSDN博客 目录 1. System V IPC 1. 消息队列 msg 消息队列的使用方法 1.1 消息队列的创建 1.2 向消息队列发送消息 1.3 从消息队列接收消息 1.4 使用msgctl函数显式地…

科技查新是什么?一文了解!

本文主要解答 1、什么是科技查新&#xff1f; 2、科技查新有哪些作用&#xff1f; 3、科技查新一般应用于什么地方&#xff1f; 4、在哪能出具正规查新报告&#xff1f; 5、科技查新流程是怎样的&#xff1f; 带着这些问题阅读这篇文章相信一定会有收获&#xff01;干活内…

【启程Golang之旅】运算符与流程控制讲解

欢迎来到Golang的世界&#xff01;在当今快节奏的软件开发领域&#xff0c;选择一种高效、简洁的编程语言至关重要。而在这方面&#xff0c;Golang&#xff08;又称Go&#xff09;无疑是一个备受瞩目的选择。在本文中&#xff0c;带领您探索Golang的世界&#xff0c;一步步地了…

如何让UE4.26使用VS2022【Windows,源码下载】

使用UE5一直用的是VS2022&#xff0c;都是因为团队需要&#xff0c;只能用UE4&#xff0c;而我电脑中拥有的UE4的版本是UE4.26以及VS2022&#xff0c;我不可能去下载VS2019来为这么一个项目&#xff0c;所以就研究了一下是哪里阻止了UE4.26不让我使用VS2022. 首先下载UE4.26源码…

记一次安卓“Low on memory“崩溃问题

前言 最近再调人脸识别算法相关demo,发现调试期间总是偶发性崩溃&#xff0c;捕获不到异常的那种&#xff0c;看日志发现原因是Low on memory&#xff0c;一开始还疑惑 App内存不够应该是OOM啊,怎么会出现这种问题&#xff0c;百思不得其解&#xff0c;直到我打开了 Android s…

在MySQL数据库中进行连接查询中的外连接查询

左外连接 特征&#xff1a;以左表为主&#xff0c;显示左表所有数据&#xff0c;相关联存在相同的值时显示对应数据&#xff0c;否则显示为NULL 语法&#xff1a;> select 表名.字段名称&#xff0c;表名.字段名称 from 表名 left join 表名 on 相关联的字段…

Linux java jni调用C++封装动态库

由于项目中java需要调用第三方提供的C动态库&#xff1b;由于第三方动态库传入的参数较多&#xff0c;还伴随着指针传入操作&#xff0c;导致java调用极为不便&#xff01;因此催生出对于第三方的C动态库进行二次封装。java调用只需按结构传入一个结构化的string即可。话不多说…

卧槽!这项目开源了!【送源码 】

随着科技的飞速发展&#xff0c;个人财务管理变得越来越重要。一个名为‘Maybe’的创新型个人财务与财富管理应用程序随之诞生&#xff0c;它以其丰富的功能和用户友好的界面受到了广大用户的关注。 现在项目方将这个价值 100万美元的个人理财应用项目开源了 Maybe Maybe应用…

吴恩达2022机器学习专项课程C2W2:2.19 sigmoid函数的替代方案 2.20如何选择激活函数 2.21 激活函数的重要性

这里写目录标题 引言sigmoid激活函数的局限1.回顾需求案例2.ReLU激活函数 常用的激活函数1.线性激活函数的解释 如何选择激活函数&#xff1f;1.选择输出层的激活函数2.选择隐藏层的激活函数 选择激活函数的总结1.输出层总结2.隐藏层总结3.TensorFlow设置激活函数 激活函数多样…

AI生成视频解决方案,降低成本,提高效率

传统的视频制作方式往往受限于高昂的成本、复杂的拍摄流程以及硬件设备的限制&#xff0c;为了解决这些问题&#xff0c;美摄科技凭借领先的AI技术&#xff0c;推出了全新的AI生成视频解决方案&#xff0c;为企业带来前所未有的视觉创新体验。 一、超越想象的AI视频生成 美摄…

大模型时代的具身智能系列专题(四)

google deepmind团队 谷歌旗下最大的两个 AI 研究机构——地处伦敦 DeepMind 与位于硅谷的 Google Brain 合并成立新部门 Google DeepMind。其将机器学习和系统神经科学的最先进技术结合起来&#xff0c;建立强大的通用学习算法。代表作有AlphaGo&#xff0c;AlphaStar&#x…