JAVA—多线程

news2024/9/23 13:29:11

        关于线程以及多线程的学习,包括创建和常用方法还有解决线程安全的措施,最后学习线程池和了解并发和并行,对于悲观锁和乐观锁的部分没有学习

目录

1.线程概述

2.多线程的创建

(1)继承Thread类

(2)实现Runnable接口

(3)实现Callable接口

3.Thread的常用方法

4.线程安全

5.线程同步

(1)同步思想概述

(2)方式一:同步代码块

(3)方式二:同步方法

(4)方式三:Lock锁

6.线程通信

7.线程池

(1)线程池概述

(2)线程池创建

(3)处理Runnable任务

(4)处理Callable任务

(5)Executors工具类实现

8.并发 并行

9.线程的生命周期


1.线程概述

线程

  • 线程(Thread)是一个程序内部的一条执行流程
  • 程序中如果只有一条执行流程,那么这个程序就是单线程的程序
  • 多线程是指从软硬件上实现的多条执行流程的技术(多线程由cpu负责调度)

2.多线程的创建

                        java通过java.lang.Thread类的对象来代表线程

(1)继承Thread类

  1. 定义线程子类继承java.lang.Thread.重写run方法
  2. 创建线程子类对象
  3. 调用线程对象的start方法启动线程(启动后执行run方法)

 多线程的注意事项
1.启动线程必须在调用start方法,不是调用run方法

2.不要把主线程任务放在启动子线程之前。

package Create_Thrread;

/*子类继承Thread线程类*/
public class MyThread extends Thread {
    //重写Run方法
    @Override
    public void run(){
        //描述线程的执行任务
        for (int i = 1; i <= 5; i++) {
            System.out.println("MyThread线程输出"+i);
        }
    }
}
package Create_Thrread;

public class ThreadTest1 {
    public static void main(String[] args) {
        //3.创建线程类对象代表线程
        Thread t = new MyThread();
        //4.启动线程(自动执行run方法)
        t.start(); //main线程 他线程

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程输出"+i);
        }

    }
}

方式一的优缺

  • 优点:编码简单
  • 缺点: 线程类已经继承Thread,无法再继承其他类,不利于功能的拓展

(2)实现Runnable接口

  1. 定义一个线程任务类实现Runnable接口,重写run()方法
  2. 创建任务对象
  3. 把任务对象交给Thread处理
  4. 调用线程对象的start方法启动线程

package Create_Thrread;
//定义一个任务类,实现Runnable接口
public class MyRunnable implements Runnable{
//2.重写run方法
    @Override
    public void run() {
        //线程执行的任务
        for (int i = 1; i <= 5; i++) {
            System.out.println("MyRunner输出"+i);
        }
    }
}
package Create_Thrread;
//实现Runnable接口
public class ThreadText2 {
    public static void main(String[] args) {
        //3.创建任务对象
        Runnable target = new MyRunnable();
        //4.把一个任务对象交给一个线程对象处理
        // public Thread(Runnable target) //有参构造器
        new Thread(target).start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程输出"+i);
        }
    }
}

 方法二的优缺

  • 优点:任务类只是实现接口,可以继续继承其他类,实现其他接口,拓展性强
  • 缺点:需要多一个Runnable对象

方法二的匿名类写法: 

  1. 创建Runnerabled的匿名内部类对象
  2. 把任务对象交给Thread处理
  3. 调用线程对象的start方法启动线程
package Create_Thrread;
//实现Runnable匿名内部类
public class ThreadText2 {
    public static void main(String[] args) {
   
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("Runner接口类实现"+i);
                }
            }
        };
        new Thread(target).start();
        
        //简化形式1:
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("Runner接口类实现"+i);
                }
            }
        }).start();
        
        //简化形式2 
        new Thread(()->{
                for (int i = 0; i < 5; i++) {
                    System.out.println("Runner接口类实现"+i);
                }
        }).start();
    }
}

(3)实现Callable接口

  假设线程执行完需要返回一些数据,前两个种方法重写的run方法均不能直接返回结果

创建步骤 

  1. 创建任务对象:定义一个类实现Callable接口,重写call方法,封装内容和返回数据把Callable类对象封装成FutureTask(线程任务对象)
  2. 把线程任务对象交给Thread对象
  3. 调用Thread对象的start方法启动线程
  4. 执行完毕通过FutureTask对象的grt方法去获取线程任务执行的结果 

package Create_Thrread;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String>{
    private int n;

    public MyCallable(int n) {
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i < n; i++) {
            sum+=i;
        }
        return "线程求出了1-"+n+"的和"+sum;
    }
}
package Create_Thrread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadText3{
    public static void main(String[] args) throws Exception {
        Callable<String> call = new MyCallable(10);
        //未来任务对象,是一个任务对象实现Runnable对象
        //可以在线程执行完毕后,用未来任务对象调用get方法获取执行后的结果
        FutureTask<String> fu = new FutureTask<>(call);
        new Thread(fu).start();
        //获取线程执行完毕后的结果
        //注意;如果执行到这里,线程还没有执行完毕
        //这里的代码会暂停等待上面的线程执行完毕才会获取结果
        System.out.println(fu.get());
    }
}

方式三的优缺点

  • 优点:线程任务类只是实现接口可以继续继承类和实现接口,拓展性强,可以在线程执行完毕后获取线程执行的结果
  • 缺点:编码复杂一点。 

3.Thread的常用方法

常用方法说明
public void run()线程的任务方法
public void start()启动线程
public String getName()获取当前线程的名称
public void setName(String name)为线程设计名称
public static Thread current获取当前执行的线程对象
public  static void sleep(long time)让当前执行的线程休眠多少毫秒后,再继续执行
public final void join()..

让调用这个方法的线程先执行完

常见构造器说明
public Thread(String name)可以为当前线程指定名称
public Thread(Runnable target)封装Runnable对象为线程对象
public Thread(Runnable target,String name)封装Runnable对象为线程对象,并指定线程名称

package Create_Thrread;

/*子类继承Thread线程类*/
public class MyThread extends Thread {
    public MyThread(String name){
        super(name); //为当前线程设置名字
    }

    public MyThread() {
    }

    @Override
    public void run(){
        Thread m = Thread.currentThread();
        //描述线程的执行任务
        for (int i = 1; i <= 5; i++) {
            System.out.println(m.getName()+"子线程输出"+i);
        }
    }
}
package Create_Thrread;

public class ThreadText4 {
    public static void main(String[] args) {
        Thread t1 = new MyThread();
        t1.start();
        System.out.println(t1.getName()); //线程默认是有名字的 Thread-0

        Thread t2 = new MyThread();
        t2.setName("二号线程"); //起名一般放置在启动之前
        t2.start();
        System.out.println(t2.getName());

        //主线程对象的名字
        //哪个线程在执行就拿到哪个线程
        Thread m = Thread.currentThread();
        System.out.println(m.getName());

        Thread t3 = new MyThread("三号线程");
        t3.start();

        for (int i = 1; i <= 5; i++) {
            System.out.println("main线程输出"+i);
        }
    }
}
package Create_Thrread;
//掌握Sleep方法,join方法
public class ThreadText5 {
    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 5; i++) {
            System.out.println(i);
            //休眠5秒
            if(i == 3){
                Thread.sleep(5000);//sleep内的数值单位为毫秒
            }
        }
        Thread t1= new MyThread("一号线程");
        t1.start();
        t1.join();//让调用这个方法的线程先执行完

        Thread t2 = new MyThread("二号线程");
        t2.start();
        t2.join();

        Thread t3 = new MyThread("三号线程");
        t3.start();
        t3.join();
    }
}

4.线程安全

线程安全问题

                多个线程在操作同一个共享资源时,可能会出现的业务安全问题

模拟线程安全问题

需求

  • 小明和小红是一对夫妻,有一个共同账号,余额为10万元,模拟两个人同时去取10万元

 存在线程安全问题!

package Create_safe;

public class Draw extends Thread{
    private Account acc;
    Thread m = Thread.currentThread();
    public Draw(Account acc,String name){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        //取钱
        DrawMonry(100000);
    }

    private void DrawMonry(int number) {
        if (acc.getMoney() >= number) {
            System.out.println(m.getName()+"取钱成功");
            acc.setMoney(acc.getMoney() - number);
            System.out.println("剩余余额"+acc.getMoney());
        }
        else{
            System.out.println("余额不足");
        }
    }


}

5.线程同步

(1)同步思想概述

解决线程安全问题的方案

线程同步的思想是控制多个线程实现先后访问共享资源 

常见方案

  • 加锁:每次只允许一个线程加锁,加锁后才能进一步访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。

(2)方式一:同步代码块

作用:把访问共享资源的核心代码给上锁,以此保证线程安全

原理:每次只允许一个线程加锁,加锁后才能进一步访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。

 注意事项:

  1. 对于同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出现bug
  2. 锁对象使用时建议使用共享资源作为锁对象,对于实例方法建议使用this作为锁对象
  3. 对于静态方法建议使用字节码对象作为锁对象
package Create_safe;

public class Account {
    private double money;; //余额

    Thread m = Thread.currentThread();
    public Account(double money) {
        this.money = money;
    }

    public Account() {
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
    public void DrawMonry(int number) {
        String name = Thread.currentThread().getName();
        synchronized (this) { //加锁 使用“楠楠”String时:不可变常量 //而使用this代表共享资源
            if (this.money >= number) {
                System.out.println(name+"取钱成功");
                money = money- number;
                System.out.println("剩余余额"+money);
            }
            else{
                System.out.println("余额不足");
            }
        }
    }
    //静态方法上锁
    public static void test(){
        synchronized (Account.class){ //使用字节码文件
            System.out.println("静态方法的锁");
        }
    }

}

(3)方式二:同步方法

作用:把访问共享资源的核心代码给上锁,以此保证线程安全,相比代码块锁的范围比较大

原理:每次只允许一个线程加锁,加锁后才能进一步访问,访问完毕后自动解锁,然后其他线程才能再加锁进来。

同步方法底层原理

  • 同步方法,其实底层也有隐式对象锁,只是锁的范围是整个方法代码,
  • 如果方法是实例方法,同步方法默认this作为锁的对象,
  • 如果方法是静态方法。同步方法默认用类名.class作为锁的对象。 
package Create_safe;

public class Account {
    private double money;; //余额

    Thread m = Thread.currentThread();
    public Account(double money) {
        this.money = money;
    }

    public Account() {
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
    public synchronized void DrawMonry(int number) {
        String name = Thread.currentThread().getName();
            if (this.money >= number) {
                System.out.println(name+"取钱成功");
                money = money- number;
                System.out.println("剩余余额"+money);
            }
            else{
                System.out.println("余额不足");
            }
        }
}

(4)方式三:Lock锁

Lock锁可以创建出锁对象进行加锁,更灵活,方便,强大

Lock是接口,不能直接实例化,可以采用实现类ReentrantLock来构建Lock锁对象


6.线程通信

线程通信适当多个线程共同操作共享的资源,线程间通过某种方式互相告知自己的状态,以相互协调,避免无效的资源竞争。

线程通信的常见模型(生产者与消费者模型

  • 生产者线程数的生产数据
  • 消费者现场负责消费生产者生产的数据
  • 注意:生成者生产的数据应该等待自己,通知消费者消费,消费者消费完数据也应该等待自己,再通知生产者生产。 (案例略)

7.线程池

(1)线程池概述

线程池就是一个复用线程的技术,是为了解决请求发起创建新线程,请求过多,影响性能

工作原理

 线程池中存有固定数量的线程(工作线程),当任务占用全部线程后,其他任务进行等待(任务队列),有线程空出进行占用,可以控制线程数量和任务数量来避免资源耗尽的风险


(2)线程池创建

ExecutorService 接口 代表线程池。可以通过ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象或者使用Executors调用方法返回不同特点的线程池对象

public ThreadPoolExecutor(    int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

参数

1.corePoolSize :指定线程池的核心线程数量

2.maximumPoolSize:指定线程池的最大线程数量(其他为临时线程)

3.keepAliveTime:指定临时线程的存活时间

4.unit:指定临时线程存活的时间单位(秒,分,时,天)

5.workQueue:指定线程池的任务队列

6.threadFactory:指定线程池的线程工厂

7.handler :指定线程池的任务拒绝策略(如何处理队列和线程都满时的新任务)

package Create_safe;

import java.util.concurrent.*;

public class ThreadPoolText1 {
    public static void main(String[] args) {
        //创建一个线程池对象
        /*int corePoolSize,
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        ThreadFactory,
        RejectedExecutionHandler handler);*/
        ExecutorService pool = new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS
                ,new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        
        
    }
}

 注意:1.临时线程在核心线程在忙,队列也满了,并且还可以创建线程时才会创建

            2.在核心线程和临时线程都在忙,任务队列也满了的时候会拒绝新任务


(3)处理Runnable任务

常用方法说明
void execute(Runnable command)执行Runnable任务
Future<T> submit(Callable<T> task)执行Callable任务,返回未来任务对象,用于获取线程返回结果
void shutdown()等全部任务执行完,再关闭线程池
List<Runnable> shutdownNow()立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务

新任务拒绝策略 

策略详解
ThreadPoolExecutor.AbortPolicy丢弃任务并抛出RejectedExecutionException异常,是默认策略
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常,不推荐
ThreadPoolExecutor.DiscardOldestPolicy抛弃队列中等待最久的任务,并把当前任务加入到队列中
ThreadPoolExecutor.CallerRunsPolicy有主线负责调用的run()方法从而绕过线程池直接执行
package Create_safe;

public class MyRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"执行成功");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
package Create_safe;

import java.util.concurrent.*;

public class ThreadPoolText1 {
    public static void main(String[] args) {
        //创建一个线程池对象
        /*int corePoolSize,
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        ThreadFactory,
        RejectedExecutionHandler handler);*/
        ExecutorService pool = new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS
                ,new ArrayBlockingQueue<>(4),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        Runnable ru = new MyRunnable();
        pool.execute(ru); //自动创建 自动处理 自动执行
        pool.execute(ru); //自动创建 自动处理 自动执行
        pool.execute(ru); //自动创建 自动处理 自动执行
        pool.execute(ru); //自动创建 自动处理 自动执行

        pool.shutdown(); //任务执行完毕后关闭线程池
        //pool.shutdownNow();//立刻关闭线程池,不管任务是否执行完毕
    }
}

(4)处理Callable任务

ExecutorService常用方法

常用方法说明
void execute(Runnable command)执行任务/命令,没有返回值,一般用来执行
Future<T>submit(Callable<T> task)执行任务,返回未来对象获取线程结果
void shutdown()等任务执行完毕后关闭线程池
List<Runnable> shutddownNow()立刻关闭,停止正在执行任务,并返回队列中未执行的任务

(5)Executors工具类实现

线程池的一个工具类,提供了很多静态方法用于返回不同特点的线程池对象

方法名称说明
public static ExecutorService newFixedThreadPool(int nThread)创建固定线程数量的线程时如果某个线程因执行异常而结束那么会补充一个新线程
public static ExecutorService newSingleThreadExecutor()创建只有一个线程的线程池,如果该线程出现异常而结束那么会补充一个新线程
public static ExecutorService newachedThreadPool()线程数量随任务增加而增加,如果线程任务执行完毕且空闲的60秒则会被回收掉。
public static ScheduledExecutorService newScheduleThreadPool(int corePoolsize)创建一个线程池,可以实现给定延迟后运行任务或者定期执行任务。

 注意:大型项目使用会出现问题(系统风险)


8.并发 并行

  • 进程:正在运行的程序(软件)就是一个独立的进程
  • 线程是属于进程的,一个进程同时运行很多个线程
  • 进程中的多个线程其实是并发和并行执行的

 并发

进程中的线程都是由CPU负责调度执行,但是CPU能同时处理线程的数量有限,为了保证全部线程都能往前执行,CPU会巡轮为系统每个线程服务,由于CPU的切换的速度很快,给我们的感觉是这些限制的同时执行,这就是并发。

并行 

  • 在同一时刻上,同时有多个线程在被CPU调度执行

多线程是并行和并发同时执行就是多线程


9.线程的生命周期

  • 线程从生到死的过程,经历的各种状态以及状态转换
  • 有利于理解多线程

                                                                                                          学习时间:2024-08-30

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

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

相关文章

【GPT】基于GPT_API_free做一个自己的gpt

最终效果 项目背景 秉持能免费就绝不花钱的原则&#xff0c;基于github项目GPT_API_free获取的gpt apikey。下面是简单的代码 import json import os import requestsopenai_url os.getenv("openaiproxy") openai_apikey os.getenv("openaikey") # 初始…

[图解]SysML和EA建模住宅安全系统-活动作为块

1 00:00:00,210 --> 00:00:04,360 下一个步骤是识别潜在的失效 2 00:00:06,850 --> 00:00:11,150 这里它是用一个块定义图来表达的 3 00:00:12,150 --> 00:00:16,790 图17.21&#xff0c;失效模式识别和因果依赖 4 00:00:19,110 --> 00:00:22,400 但是这个块定义…

【AutoX.js】定时器 Timers

文章目录 原文&#xff1a;https://blog.c12th.cn/archives/36.html定时器 Timers笔记实例 最后 原文&#xff1a;https://blog.c12th.cn/archives/36.html 定时器 Timers 笔记 JavaScript Date 参考手册 时间戳 //当前时间戳 log(Math.round(new Date() / 1000));当前星期 …

C语言小tip之函数递归

hello&#xff0c;各位小伙伴们今天我们来学习一下函数递归。 什么是函数递归呢&#xff1f;简单来说就是函数自己来调用自己。函数递归的主要思想是把大事化小&#xff0c;递归包含两层方面&#xff1a;1、递推 2、回归 在使用函数递归的时候要注意包含两个限制条件&#…

SCI英文期刊发表流程

目录 一、撰写初稿二、预审三、英文查重四、Cover letter和Highlights五、英文语法待续 一、撰写初稿 英文好的话应该直接写英文&#xff0c;因为中英文的写法不一样。 而且在这一步把格式修改好&#xff0c;初稿的最终版 二、预审 可以让同学、老师帮看论文&#xff0c;或者…

[Algorithm][综合训练][字符编码][最少的完全平方数][游游的字母串]详细讲解

目录 1.字符编码1.题目链接2.算法原理详解 && 代码实现 2.最少的完全平方数1.题目链接2.算法原理详解 && 代码实现 3.游游的字母串1.题目链接2.算法思路详解 && 代码实现 1.字符编码 1.题目链接 字符编码 2.算法原理详解 && 代码实现 解法&…

Carla自动驾驶仿真十:Carlaviz三维可视化平台搭建

文章目录 前言一、环境准备1、docker安装2、websocket-client安装3、carlaviz代码下载 二、carlaviz使用1、打开carla客户端2、输入启动命令3、进入carlaviz4、修改manual_control.py脚本5、运行manual_control.py脚本6、运行carlaviz官方脚本&#xff08;推荐&#xff09; 前言…

数据库之心:MySQL 探索(一)mysql的安装和基本介绍

欢迎来到我们的MySQL博客&#xff01;在这里&#xff0c;我们将深入探讨MySQL数据库系统的各个方面&#xff0c;包括基础知识、优化技巧、实践案例以及最新的行业趋势。 目录 前言 什么是数据库&#xff1f; 数据库产品 MySQL安装 解压 配置 添加环境变量 初始化MySQL …

计算机毕业设计PySpark+Scrapy高考推荐系统 高考志愿填报推荐系统 高考爬虫 协同过滤推荐算法 Vue.js Django Hadoop 大数据毕设

目  录 第1章 绪论 1.1 研究背景 1.2 国内外现状 1.2.1 国外研究现状 1.2.2 国内研究现状 1.3 主要研究内容 1.4 论文框架结构 第2章 相关开发技术与理论 2.1 前端技术 1&#xff0e;Vue框架技术 2&#xff0e;Element-Plus 2.2 后端技术 1&#xff0e…

linux下cpu多核运行程序以及运行时间统计

一、多核心运行程序 在linux下我们可以指定线程或者进程运行在指定的cpu核心上&#xff0c;操作方法如下&#xff1a; 1&#xff09;运行进程指定cpu核心 taskset -c 2 ./app //-c指定运行的cpu核心号&#xff0c;从0计数&#xff0c;查看效果如下&#xff1a; 2&#xff09…

C++map容器中operator[ ]的实现原理

目录 一、operator[ ]函数介绍 二、insert函数介绍 三、operator[ ]函数实现原理 四、operator[ ]函数功能 一、operator[ ]函数介绍 mapped_type& operator[] (const key_type& k);在map容器中存储的是一个键值对value_type&#xff0c;其本质是pair<const key…

maya python调试(pycharm)

maya里面调试代码一直用的print。遇到复杂点的类就感觉有点束手束脚的&#xff0c;因此整理了一下maya调试的一些方法 1.万能的pdb调试 pdb 有2种用法 1.非侵入式方法 &#xff08;不用额外修改源代码&#xff0c;在命令行下直接运行就能调试&#xff09; 常规用法&#xf…

APP长文本内容编辑器功能实现方案

背景 CSDN APP 中原有编辑器页面为纯H5适配&#xff0c;整体用户交互体验差&#xff0c;如何优化APP端编辑器用户体验是我们团队需要思考的问题。下面我们以iOS为例展开讨论。 一、方案调研 我们分析了几款国内内容发布的APP&#xff0c;如知乎、今日头条、简书&#xff0c;…

有了它 一键掌握Vue新版本!

声明&#xff1a;此篇为 ai123.cn 原创文章&#xff0c;转载请标明出处链接&#xff1a;https://ai123.cn/#1 你是否也在为Vue生态中的快速更新而焦头烂额&#xff1f;ue 3.4版本发布&#xff0c;带来模板解析器重写和响应系统重构&#xff0c;提升了性能和开发体验。测试框架如…

《深入理解JAVA虚拟机(第2版)》- 第3章 - 学习笔记

第3章 垃圾收集器与内存分配策略 3.1 概述 垃圾收集器要完成三件事情&#xff1a; 什么样的内存需要回收什么时候回收如何回收 垃圾收集器主要关注的区域是&#xff1a;Java堆和方法区。因为程序计数器、虚拟机栈、本地方法栈是线程私有的&#xff0c;随着线程的结束所使用的…

2d椭圆拟合学习

算法来自论文《 Direct Least Square Fitting of Ellipses》 《NUMERICALLY STABLE DIRECT LEAST SQUARES FITTING OF ELLIPSES》 相关文章 论文阅读&#xff1a;直接拟合椭圆 Direct Least Square Fitting of Ellipseshttps://zhuanlan.zhihu.com/p/645391510Fitting Elli…

rsyslog交叉编译

文章目录 1、依赖库列表2、编译建议3、编译3.1、编译libestr3.2、编译libfastjson3.3、编译zlib3.4、编译libuuid3.5、编译libgpg-error3.6、编译libgcrypt3.7、编译openssl3.8、编译curl3.9、编译rsyslog该文档描述了如何交叉编译rsyslog到arm64嵌入式平台。 1、依赖库列表 li…

UE5开发——射击武器类拾取

整体框架&#xff1a; 拾取武器 要在 Unreal Engine 5 (UE5) 中实现一个按 E 键拾取武器的功能&#xff0c;您可以遵循以下步骤&#xff1a; ### 步骤 1: 创建拾取物品的基础类 1. 在 Content Browser 中创建一个新的 C 类&#xff0c;继承自 AActor 或者 AStaticMeshActor。…

pytorch交叉熵损失函数

nn.CrossEntropyLoss 是 PyTorch 中非常常用的损失函数,特别适用于分类任务。它结合了 nn.LogSoftmax 和 nn.NLLLoss(负对数似然损失)的功能,可以直接处理未经过 softmax 的 logits 输出,计算预测值与真实标签之间的交叉熵损失。 1. 交叉熵损失的原理 交叉熵损失衡量的是…

Visual Studio Code离线汉化

从官网下载Visual Studio Code安装包后&#xff0c; 下载Visual Studio Code&#xff1a;https://code.visualstudio.com/ 若因网络等问题无法在线安装语言包&#xff0c;可以尝试离线安装&#xff1a; 从官网下载语言包&#xff1a; Extensions for Visual Studio family …