【多线程(四)】线程状态介绍、线程池基本原理、Executors默认线程池、ThreadPoolExecutor线程池

news2024/12/25 9:19:17

文章目录

  • 4.线程池
    • 4.1状态介绍
    • 4.2线程的状态-练习1
    • 4.3线程的状态-练习2
    • 4.4线程的状态-练习3
    • 4.5线程池-基本原理
    • 4.6线程池-Executors默认线程池
    • 4.7线程池-Executors创建指定上限的线程池
    • 4.8线程池-ThreadPoolExecutor
    • 4.9线程池-参数详解
    • 4.10线程池-非默认任务拒绝策略
    • 总结

4.线程池

4.1状态介绍

当线程被创建并启动以后,它既不是一启动就进入执行状态,也不是一直处于执行状态。线程对象不同的时期不同的状态。那么Java中的线程存在哪几种状态呢?状态被定义在了 java.lang.Thread.State 枚举类中,State 枚举类的源码如下:
在这里插入图片描述
通过源码我们可以看到 Java中的线程存在六种状态,每种线程状态的含义如下:
在这里插入图片描述
各个状态的转换,如下图所示:
在这里插入图片描述

4.2线程的状态-练习1

  • 目的:本案例主要演示 TIME_WAITING 的状态转换

  • 需求:编写一段代码,依次显示一个线程的这些状态:NEW -> RUNNABLE -> TIME_WAITING -> RUNNABLE -> TERMINATED
    为了简化我们得开发,本次我们使用匿名内部类结合lambda表达式的方式使用多线程。

  • 代码实现

public class ThreadStateDemo01 {
    public static void main(String[] args) throws InterruptedException {

        //定义一个内部线程
        Thread thread = new Thread(()->{
            System.out.println("2.执行thread.start()之后,线程的状态:"+
                    Thread.currentThread().getState());
        });
        try {
            //休眠100毫秒
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("4.执行Thread.sleep(long)完成之后,线程的状态:"+
                Thread.currentThread().getState());

        //获取start()之前的状态
        System.out.println("1.通过new初始化一个线程,但是还没有start()之前,线程的状态:"+
                thread.getState());

        //启动线程
        thread.start();

        //休眠50毫秒
        Thread.sleep(50);

        //因为thread1需要休眠100毫秒,所以在第50毫秒,thread处于sleep状态
        System.out.println("3.执行Thread.sleep(long)时,线程的状态:"+thread.getState());

        //thread1和main线程主动休眠 150毫秒,所以在第150毫秒,thread早已执行完毕
        Thread.sleep(100);

        System.out.println("5.线程执行完毕之后,线程的状态:"+thread.getState()+"\n");
    }
}
  • 控制台输出
    在这里插入图片描述

4.3线程的状态-练习2

  • 目的:本案例 主要演示 WAITING 的状态转换。
  • 需求:编写一段代码,依次显示一个线程的这些状态: NEW -> RUNNABLE -> WAITING -> RUNNABLE -> TERMINATED
  • 代码实现
public class ThreadStateDemo02 {
    public static void main(String[] args) throws InterruptedException {

        //定义一个对象,用于加锁和解锁
        Object obj = new Object();

        //定义一个内部线程
        Thread thread1 = new Thread(()->{
            System.out.println("2.执行thread.start()之后,线程的状态"+
                    Thread.currentThread().getState());
            synchronized (obj){
                try {

                    //thread1需要休眠100毫秒
                    Thread.sleep(100);

                    //thread1100毫秒之后,通过wait()方法释放obj对象是锁
                    obj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("4.被object.notify()方法唤醒之后,线程的状态"+
                    Thread.currentThread().getState());

        });

        //获取start()之前的状态
        System.out.println("1.通过new初始化一个线程,但是还没有start()之前,线程的状态"+
                thread1.getState());

        //启动线程
        thread1.start();

        //main线程休眠 150毫秒
        Thread.sleep(150);

        //因为thread1在第100毫秒进入 wait 等待状态,所以第150毫秒肯定可以获取其状态
        System.out.println("3.执行object.wait()时,线程的状态:"+thread1.getState());

        //声明另一个线程进行解锁
        new Thread(()->{
            synchronized (obj){
                //唤醒等待的线程
                obj.notify();
            }
        }).start();

        //main线程休眠10毫秒等待thread1线程能够苏醒
        Thread.sleep(10);

        //获取 thread1运行结束之后的状态
        System.out.println("5.线程执行完毕之后,线程的状态:"+thread1.getState()+"\n");

    }
}
  • 控制台输出结果
    在这里插入图片描述

4.4线程的状态-练习3

  • 目的:本案例主要演示BLOCKED的状态转换
  • 需求:编写一段代码,依次显示一个线程的这些状态: NEW -> RUNNABLE ->BLOCKED -> RUNNABLE -> TERMINATED
  • 代码实现
public class ThreadStateDemo03 {
    public static void main(String[] args) throws InterruptedException {

        //定义一个对象,用于加锁和解锁
        Object obj2 = new Object();

        //定义一个线程,先抢占了 obj2 对象的锁
        new Thread(()->{
            synchronized (obj2){
                try {
                    Thread.sleep(100);//第一个线程要持有锁100毫秒
                    obj2.wait();//然后通过 wait()方法进行等待状态,并释放obj2的对象锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        //定义目标线程,获取等待获取obj2的锁
        Thread thread = new Thread(()->{
            System.out.println("2.执行thread.start()之后,线程的状态"+
                    Thread.currentThread().getState());
            synchronized (obj2){
                try {
                    Thread.sleep(100);//thread3要持有对象锁100毫秒
                    obj2.notify();//然后通过notify()方法唤醒所有在obj2上等待的线程继续执行后续操作

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("4.阻塞结束后,线程的状态:"+Thread.currentThread().getState());

        });

        //获取start()之前的状态
        System.out.println("1.通过new初始化一个线程,但是还没有thread.start()之前,线程的状态:"+
                thread.getState());

        //启动线程
        thread.start();

        //先等100毫秒
        Thread.sleep(100);

        //第一个线程释放锁至少需要100毫秒,所以在第50毫秒时,thread正在因等待obj的对象而阻塞
        System.out.println("3.因为等待锁而阻塞时,线程的状态:"+thread.getState());

        //再等 300 毫秒
        Thread.sleep(300);

        //两个线程的执行时间加上之前等待的50毫秒总共是250毫秒,所以第300毫秒,所有的线程都已经执行完毕

        System.out.println("5.线程执行完毕之后,线程的状态:"+thread.getState());
    }
}
  • 控制台输出
    在这里插入图片描述

4.5线程池-基本原理

  • 概述
    提到池,大家应该能想到的就是水池。水池就是一个容器,在该容器中存储了很多的水。那么什么是线程池呢?线程池也是可以看做成一个池子,在该池子中存储很多个线程

  • 线程存在的意义
    系统创建一个线程的成本是比较的,因为它涉及到与操作系统交互,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理对系统资源的消耗,这样就有点“舍本逐末”了。针对这一种情况,为了提高性能,我们就可以采用线程池。线程池在启动的时候,会创建大量空闲线程当我们向线程池提交任务的时候,线程池就会启动一个线程来执行该任务,等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。

  • 线程池的设计思路
    1.准备一个任务容器
    2.一次性启动多个(2个)消费者线程
    3.刚开始任务容器是空的,所以线程都在wait
    4.直到一个外部线程向这个任务容器中扔了一个“任务”,就会有一个消费者线程被唤醒。
    5.这个消费者线程取出“任务”,并且执行这个任务,执行完毕之后,继续等待下一次任务的到来

4.6线程池-Executors默认线程池

  • 概述:JDK 对线程池也进行了相关实现,在真正企业开发中我们也很少去自定义线程池,而是使用JDK中自带的线程池。
  • 我们也可以使用 Exexutors 中所提供的静态方法来创建线程池
    • static ExecutorService newCachedThreadPool() 创建一个默认的线程池
    • static newFixedThreadPool(int nThreads) 创建一个指定最多线程数量的线程池
  • 代码实现
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });
            Thread.sleep(2000);
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });

        executorService.shutdown();
    }
}
  • 控制台输出
    在这里插入图片描述

4.7线程池-Executors创建指定上限的线程池

使用Executors中所提供的的静态方法来创建线程池

  • static ExecutorService newFixedThreadPool(int nThreads) : 创建一个指定最多线程数量的线程池
  • 代码实现
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyThreadPoolDemo2 {
    public static void main(String[] args) {

        //参数不是初始值,而是最大值,初始值为0
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });

        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行了");
        });

        executorService.shutdown();
    }
}
  • 控制台输出
    在这里插入图片描述

4.8线程池-ThreadPoolExecutor

创建线程池对象

  • ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);
  • 代码实现
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"在执行了");
    }
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo3 {
    public static void main(String[] args) {
        //  参数一:核心线程数量
        //  参数二:最大线程数
        //  参数三:空闲线程最大存活时间
        //  参数四:时间单位---TimeUnit
        //  参数五:任务队列---让任务在队列中等着,等有线程空闲了,再从这个队列中获取任务并执行
        //  参数六:创建线程工厂---按照默认的方式创建线程对象
        //  参数七:任务的拒绝策略--1.什么时候拒绝任务 当提交的任务>池子中最大线程数量+队列容量
                                //2.如何拒绝
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                2,
                5,
                2,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        pool.shutdown();
    }
}
  • 控制台输出
    在这里插入图片描述

4.9线程池-参数详解

在这里插入图片描述
在这里插入图片描述

4.10线程池-非默认任务拒绝策略

  • RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,它下面存在4个子类。
    在这里插入图片描述
    注:明确线程池对多可执行的任务数 = 队列容量 + 最大线程数
  • 案例演示1:演示ThreadPoolExecutor.AbortPolicy任务处理策略
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo4 {
    public static void main(String[] args) {
        //  参数一:核心线程数量
        //  参数二:最大线程数
        //  参数三:空闲线程最大存活时间
        //  参数四:时间单位---TimeUnit
        //  参数五:任务队列---让任务在队列中等着,等有线程空闲了,再从这个队列中获取任务并执行
        //  参数六:创建线程工厂---按照默认的方式创建线程对象
        //  参数七:任务的拒绝策略--1.什么时候拒绝任务 当提交的任务>池子中最大线程数量+队列容量
                                //2.如何拒绝
        // 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                1,
                3,
                20,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用AbortPolicy这个任务处理策略的时候,就会抛出异常
        for (int i= 0;i < 5;i++) {
            pool.submit(()->{
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }

        pool.shutdown();
    }
}

  • 控制台输出
    在这里插入图片描述

    • 控制台报错,仅仅执行了4个任务,有一个任务被丢弃了。
  • 案例演示2:演示ThreadPoolExecutor.DiscardPolic任务处理策略

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo6 {
    public static void main(String[] args) {
        //  参数一:核心线程数量
        //  参数二:最大线程数
        //  参数三:空闲线程最大存活时间
        //  参数四:时间单位---TimeUnit
        //  参数五:任务队列---让任务在队列中等着,等有线程空闲了,再从这个队列中获取任务并执行
        //  参数六:创建线程工厂---按照默认的方式创建线程对象
        //  参数七:任务的拒绝策略--1.什么时候拒绝任务 当提交的任务>池子中最大线程数量+队列容量
                                //2.如何拒绝
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                1,
                3,
                20,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy());
        // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用DiscardPolicy这个任务处理策略的时候,控制台不会报错
        for (int i= 0;i < 5;i++) {
            int y = i;
            pool.submit(()->{
                System.out.println(Thread.currentThread().getName()+"在执行了"+"----"+ y);
            });
        }

        pool.shutdown();
    }
}
  • 控制台输出
    在这里插入图片描述

    • 控制台没有报错,仅仅执行了4个任务,有一个任务被丢弃了。
      -案例演示3:演示ThreadPoolExecutor.DiscardOldestPolicy任务处理策略
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo5 {
    public static void main(String[] args) {
        //  参数一:核心线程数量
        //  参数二:最大线程数
        //  参数三:空闲线程最大存活时间
        //  参数四:时间单位---TimeUnit
        //  参数五:任务队列---让任务在队列中等着,等有线程空闲了,再从这个队列中获取任务并执行
        //  参数六:创建线程工厂---按照默认的方式创建线程对象
        //  参数七:任务的拒绝策略--1.什么时候拒绝任务 当提交的任务>池子中最大线程数量+队列容量
                                //2.如何拒绝
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                1,
                3,
                20,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        for (int i= 0;i < 5;i++) {
            // 定义一个变量,来指定指定当前执行的任务;这个变量需要被final修饰
             final int y = i;
            pool.submit(()->{
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务" + y);
            });
        }

        pool.shutdown();
    }
}
  • 控制台输出
    在这里插入图片描述

    • 由于任务1在线程池中等待时间最长,因此任务1被丢弃。
      案例演示4:演示ThreadPoolExecutor.CallerRunsPolicy任务处理策略
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyThreadPoolDemo7 {
    public static void main(String[] args) {
        //  参数一:核心线程数量
        //  参数二:最大线程数
        //  参数三:空闲线程最大存活时间
        //  参数四:时间单位---TimeUnit
        //  参数五:任务队列---让任务在队列中等着,等有线程空闲了,再从这个队列中获取任务并执行
        //  参数六:创建线程工厂---按照默认的方式创建线程对象
        //  参数七:任务的拒绝策略--1.什么时候拒绝任务 当提交的任务>池子中最大线程数量+队列容量
                                //2.如何拒绝
        /**
         * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
         */
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                1,
                3,
                20,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(1),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        // 提交5个任务
        for (int i= 0;i < 5;i++) {
            pool.submit(()->{
                System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
            });
        }

        pool.shutdown();
    }
}
  • 控制台输出
    在这里插入图片描述

    • 通过控制台的输出,我们可以看到次策略没有通过线程池中的线程执行任务,而是直接调用任务的run()方法绕过线程池直接执行。

总结

本文从线程的六种状态切入,用代码实现了六种状态的转换清晰的反映在控制台,然后详细的介绍了线程池的基本原理,并介绍了默认的Executors线程池,以及ThreadPoolExecutor线程池,最后还介绍了ThreadPoolExecutor线程池的各个参数以及其他的非默认任务拒绝策略
更新不易,希望大家多多支持!!在这里插入图片描述

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

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

相关文章

用一个原始密码针对不同软件生成不同密码并保证相对安全

使用一个密码并在数据泄漏时保护自己的其它账号 关于密码 现在好多软件&#xff0c;好多网站都需要我们设置密码&#xff0c;这个时候我们的处理办法一般分为2种。 对不同的软件设置不同的密码&#xff0c;这种理论上是最安全的&#xff0c;但是记不住啊&#xff0c;所以不实…

微信小程序自动化框架的搭建python+minium

说明 公司要求做小程序的自动化&#xff0c;网上找各种资料&#xff0c;最后确定使用腾讯自研的框架minium&#xff0c;虽然版本已经不继续维护更新了&#xff0c;但是不影响我们使用来做自动化开发。 minium提供一个基于unittest封装好的测试框架&#xff0c;MiniTest是mini…

Acrel-1200分布式光伏运维平台

行业现状 “十四五”期间&#xff0c;随着“双碳”目标提出及逐步落实&#xff0c;本就呈现出较好发展势头的分布式光伏发展有望大幅提速。就“十四五”光伏发展规划&#xff0c;国家发改委能源研究所可再生能源发展中心副主任陶冶表示&#xff0c;“双碳”目标意味着国家产业…

系列学习 SpringCloud-Alibaba 框架之第 4 篇 —— Sentinel 高可用流量控制组件

1、概念 Sentinel 是由阿里巴巴开发的开源项目&#xff0c;面向分布式微服务架构的轻量级高可用流量控制组件。以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保护等多个维度帮助用户保护服务的稳定性。可以说&#xff0c;Sentinel 就是取代 Hystrix 组件的。因为 …

H3C WX2510h无线控制器如何网关式部署无线网络

环境&#xff1a; H3C-WX2510H AC控制器 H3C Comware Software, Version 7.1.064, Release 5457 AP H3CWA6320-C 问题描述&#xff1a; H3C wx2510h无线控制器如何网关式部署无线网络 解决方案&#xff1a; 1.配置DHCP服务&#xff0c;开启vlan1为DHCP服务器 2.新建地址…

Spring-boot初级

一、springboot介绍 Spring Boot 是由 Pivotal 团队提供的基于 Spring 的全新框架&#xff0c;其设计目的是为了简化 Spring 应用的搭建和开发过程。该框架遵循『约定大于配置』原则&#xff0c;采用特定的方式进行配置&#xff0c;从而使开发者无需定义大量的 XML 配置。通过…

表、栈和队列及其C语言实现

1、抽样数据类型 程序设计的基本法则之一是例程不应该超过一页。这可以通过把程序分割为一些模块(module)来实现。每个模块是一个逻辑单元并执行某个特定的任务&#xff0c;它通过调用其他模块而本身保持很小。模块化有几个优点。首先&#xff0c;调试小程序比调试大程序容易得…

ALM研发管理中规则库的配置与使用

1.规则库简介 规则库就是描述某领域内知识的产生式规则的集合&#xff0c;而规则往往是由一个具体的业务逻辑具象而来&#xff0c;它通常是很具体的&#xff0c;有着明确的处理逻辑&#xff08;即将输入数据经过一系列逻辑处理&#xff0c;输出处理后的结果&#xff09;。 2.规…

从一个 issue 出发,带你玩图数据库 NebulaGraph 内核开发

如何 build NebulaGraph&#xff1f;如何为 NebulaGraph 内核做贡献&#xff1f;即便是新手也能快速上手&#xff0c;从本文作为切入点就够了。 NebulaGraph 的架构简介 为了方便对 NebulaGraph 尚未了解的读者也能快速直接从贡献代码为起点了解它&#xff0c;我把开发、贡献内…

synchronized详解

什么是线程安全问题? 简单来说就是当多个线程同时访问某个方法时,这个方法无法按照我们预期的行为来执行&#xff0c;那么我们认为这个方法是线程不安全的 导致线程不安全的主要原因 1.原子性 什么是原子性 比如在数据库事务的ACID特性中 当前操作中包含多个数据库事务操…

DolphinDB 诚挚招募实施伙伴

随着 DolphinDB 业务发展&#xff0c;为满足迅速增长的客户需求&#xff0c;我们现正式启动“实施伙伴招募计划”。DolphinDB 客户已经涵盖7家Top 10券商、头部公募及私募基金、知名银行、交易所、世界500强制造业客户、标杆能源企业等&#xff0c;我们非常期待和欢迎实施伙伴们…

毕业设计 基于stm32的智能平衡小车 - 单片机 物联网嵌入式

文章目录0 前言1 项目背景2 设计思路3 硬件设计4 软件设计4.1 核心PID控制4.2 直立控制程序设计4.3 速度控制程序设计4.4 方向控制程序设计4.5 关键代码5 最后0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点…

32、Java高级特性——日期操作类、Date类、SimpleDateFormat类、Calendar类

目录 一、日期操作类 二、Date类 1、Date类中的构造方法 2、获取系统当前时间 三、SimpleDateFormat类 1、SimpleDateFormat类中的构造方法 2、format(Date date,StringBuffer toAppendTo,FieldPosition pos)方法 四、Calendar类 1、Calendar类中的构造方法 2、Cal…

[附源码]Python计算机毕业设计SSM家政信息管理平台(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Removing the Bias of Integral Pose Regression 阅读笔记

消除积分姿态回归的偏差 ICCV2021 论文链接 补充材料链接 参考链接 摘要&#xff1a; 尽管姿态估计的回归法更直观&#xff0c;但由于热图法的优越性能&#xff0c;使其在2D人体姿态估计中占主导地位。积分回归法在架构上使用隐式热图&#xff0c;拉近了热图法与回归法。这就…

GraalVM + Springboot3 + IDEA 在widow10 上完成构建本地化服务

GraalVM是开发人员编写和执行Java代码的工具。具体来说&#xff0c;GraalVM是由Oracle创建的Java虚拟机&#xff08;JVM&#xff09;和Java开发工具包&#xff08;JDK&#xff09;。它是一个高性能的运行时&#xff0c;可以提高应用程序的性能和效率。 GraalVM的目标包括&…

【数据结构】哈希表

目录 一、哈希函数的引入 二、解决哈希冲突的思路 2.1基于闭散列的思路 2.2基于开散列的思路 2.3负载因子 三、关于哈希函数的设计 四、基于拉链法实现哈希表 4.1哈希表的内部构造 4.2插入操作 4.3扩容操作 4.4搜索操作 4.5删除操作 哈希表其实就是基于数组衍生而来…

深度解读面试题:链表中环的入口结点(附代码,可过在线OJ)

在解读“链表中环的入口结点”前&#xff0c;我认为有必要明白关于它的一些用于打基础的问题&#xff08;相交链表、判断链表中是否存在环&#xff09; 相交链表 题目&#xff1a; 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点…

快收藏!!整理了100个Python小技巧!!

下面&#xff0c;我就给大家分享100个Python小技巧&#xff0c;帮助大家更好的了解和学习Python&#xff0c;欢迎收藏、关注&#xff0c;点赞支持&#xff01; ▍1、for循环中的else条件 这是一个for-else方法&#xff0c;循环遍历列表时使用else语句。下面举个例子&#xff…

根据平均值列出记录

AANSI SQL包括几个聚合函数&#xff0c;使您可以对一组值进行计算以将其结果作为单个值返回。他们包括Count(), Min(), Max(), Sum() and AVG(),以及其他。默认情况下&#xff0c;聚合函数适用于所有行&#xff0c;但是您可以通过将WHERE子句应用于SELECT语句来缩小字段的范围。…