一文掌握线程池实现原理

news2025/1/13 11:46:34

线程池简介

Java在使用线程执行程序时,需要调用操作系统内核的API创建一个内核线程,操作系统要为线程分配一系列的资源;当该Java线程被终止时,对应的内核线程也会被回收。因此,频繁的创建和销毁线程需要消耗大量资源。此外,由于CPU核数有限,大量的线程上下文切换会增加系统的性能开销,无限制地创建线程还可能导致内存溢出。为此,Java在JDK1.5版本中引入了线程池。

线程池是一种重用线程的机制,用于提高线程的利用率和管理线程的生命周期,常用于多线程编程异步编程

线程池的优点:

  • 降低资源消耗:线程池中的线程可以重复使用,避免因频繁创建和销毁线程而带来的性能开销。
  • 提高响应速度:向线程池中提交任务时,无需创建线程即可执行任务处理。
  • 方便线程管理:线程池可以对其中的线程进行统一管理、监控,避免因大量创建线程而导致内存溢出。

线程池实现原理

Java线程池的核心实现类为ThreadPoolExecutor。

ThreadPoolExecutor依赖关系

ThreadPoolExecutor依赖关系图:

ThreadPoolExecutor依赖关系图

其中:

  • Executor(接口):该接口线程池处理任务的顶级接口,定义了一个用于执行任务的方法execute(Runnable command)。其中参数command为实现了Runnable或Callable接口的Task任务。
  • ExecutorService(接口):该接口继承了Executor接口,它扩展了Executor接口,并添加了一些管理线程池的方法(如:提交任务、关闭线程池等)。
  • AbstractExecutorService(抽象类):该类实现了ExecutorService接口。

构造函数

如ThreadPoolExecutor依赖关系图所示,ThreadPoolExecutor类提供了四个构造函数,其中原始的构造函数(另外三个构造函数由原始构造函数衍生而来):

线程池的构造函数:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

核心参数:

  • corePoolSize:线程池中的核心线程数,当向线程池中提交任务时,线程池创建新线程执行任务处理,直到当前线程数达到corePoolSize设定的值。此时提交的新任务会被添加到阻塞队列中,等待其他线程执行完成。默认情况下,空闲的核心线程并不会被回收(即:属性allowCoreThreadTimeOut的值默认为false),如果需要回收空闲的核心线程,设置属性allowCoreThreadTimeOut的值为true即可。

  • maximumPoolSize:线程池中的最大线程数,当线程池中的线程数达到corePoolSize设定的值且核心线程均被占用时,如果阻塞队列已满并向线程池中继续提交任务,则创建新的线程执行任务处理,直到当前线程数达到maximumPoolSize设定的值。

  • keepAliveTime:线程池中线程的存活时间,该参数只会在线程数大于corePoolSize设定的值时才生效,即:非核心线程的空闲时间超过keepAliveTime设定的值时会被回收。

  • unit:线程池中参数keepAliveTime的时间单位,默认为TimeUnit.MILLISECONDS(毫秒),其他时间单位:

    • TimeUnit.NANOSECONDS(纳秒)
    • TimeUnit.MICROSECONDS(微秒)
    • TimeUnit.MILLISECONDS(毫秒)
    • TimeUnit.SECONDS(秒)
    • TimeUnit.MINUTES(分钟)
    • TimeUnit.HOURS(小时)
    • TimeUnit.DAYS(天)
  • workQueue:线程池中保存任务的阻塞队列,当线程池中的线程数达到corePoolSize设定的值,继续提交任务时会将任务添加到阻塞队列中等待。默认为LinkedBlockingQueue,可选择的阻塞队列:

    • LinkedBlockingQueue(基于链表实现的阻塞队列)。
    • ArrayBlockingQueue(基于数组实现的阻塞队列)。
    • SynchronousQueue(只有一个元素的阻塞队列)。
    • PriorityBlockingQueue(实现了优先级的阻塞队列)。
    • DelayQueue(实现了延迟功能的阻塞队列)。
  • threadFactory:线程池中创建线程的工厂,可以通过自定义线程工厂的方式为线程设置一个便于识别的线程名,默认为DefaultThreadFactory。

  • handler:线程池的拒绝策略(又称饱和策略),当线程池中的线程数达到maximumPoolSize设定的值且阻塞队列已满时,继续向线程池中提交任务,就会触发拒绝策略,默认为AbortPolicy。可选的拒绝策略:

    • AbortPolicy:丢弃任务,并抛出RejectedExecutionException异常。
    • DiscardPolicy:丢弃任务,不抛出异常。
    • DiscardOldestPolicy:丢弃队列中最早的未处理的任务,执行当前任务。
    • CallerRunsPolicy:由调用者所在的线程来执行任务。

线程池生命周期

在ThreadPoolExecutor类中定义了线程池的五种状态。

源码如下:

// ctl共32位,其中高3位表示线程池运行状态,低29位表示线程池中的线程数量。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;

// 二进制为0001 1111 1111 1111 1111 1111 1111 1111
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// runState is stored in the high-order bits
// RUNNING状态:二进制为111 00000000000000000000000000000
private static final int RUNNING    = -1 << COUNT_BITS;
// SHUTDOWN状态:二进制为000 00000000000000000000000000000
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// STOP状态:二进制为001 00000000000000000000000000000
private static final int STOP       =  1 << COUNT_BITS;
// TIDYING状态:二进制为010 00000000000000000000000000000
private static final int TIDYING    =  2 << COUNT_BITS;
// TERMINATED状态:二进制011 00000000000000000000000000000
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 线程池中的线程数量
private static int workerCountOf(int c)  { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }

其中:

ctl是一个AtomicInteger类型的变量:

  • 高3位表示线程池的运行状态。
  • 低29位表示线程池中的线程数量。

线程池运行状态:

  • RUNNING:处于RUNNING状态的线程池会接受新任务提交,同时也会处理任务队列中的任务。
  • SHUTDOWN:处于SHUTDOWN状态的线程池不会接受新任务提交,但是会处理任务队列中的任务。
  • STOP:处于STOP状态的线程池不会接受新任务提交,也不会处理任务队列中的任务,并且会中断正在执行的任务。
  • TIDYING:当所有的任务已终止、workerCount数量为0时,线程池会进入TIDYING状态,进入TIDYING状态会执行钩子方法terminated()。
  • TERMINATED:执行完钩子方法terminated()后进入TERMINATED状态,此时线程池已终止。

线程池状态转换,如图所示:

线程池状态转换图

工作线程

Worker(工作线程)是ThreadPoolExecutor的一个内部类,它继承了
AbstractQueuedSynchronizer(即:AQS)并实现了Runnable接口。其中:

  • 通过继承AbstractQueuedSynchronizer类,可以控制在主线程中调用shutdown()方法时不会中断正在执行的工作线程。注意:调用shutdownNow()时会中断正在执行的工作线程。
  • 通过实现Runnable接口,可以在run()方法中调用ThreadPoolExecutor#runWorker方法执行任务处理。

提交任务

向线程池中提交任务的方式有两种:

  • 调用execute()方法。
  • 调用submit()方法。

execute与submit的区别:

  • 1)execute是Executor接口的方法,submit是ExecuteService接口的方法。
  • 2)execute的入参为Runnable,submit的入参可以为Runnable、Callable、Runnable和一个返回值。
  • 3)execute没有返回值,submit有返回值。
  • 4)异常处理:execute会直接抛出异常,submit会在获取结果时抛出异常,如果不获取结果,submit不抛出异常。

以execute()方法为例,ThreadPoolExecutor实现了Executor接口定义的execute(Runnable command) 方法,该方法的主要作用是将任务提交到线程池中的执行。

execute(Runnable command) 方法源码如下:

public void execute(Runnable command) {
    // 如果command为null,则抛出空指针异常
    if (command == null)
        throw new NullPointerException();
    // 获取ctl变量值,ctl低29位用来表示线程池中的线程数量。
    int c = ctl.get();
    // 如果线程池中的线程数小于设定的核心线程数,则将任务封装成Worker线程并调用其start()方法启动线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 如果线程池处于运行状态并向任务队列中添加任务成功,则执行如下逻辑(此处线程池中的线程数大于等于核心线程数)
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 继续判断线程池处于运行状态,如果线程池不是运行状态且从线程池中移除任务成功,则执行拒绝策略
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 如果线程池中线程数为0,则创建Worker线程并执行任务
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 如果创建Worker线程失败,则执行拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}

ThreadPoolExecutore#xecute()方法执行逻辑,如图所示:

ThreadPoolExecutor#execute()方法执行逻辑

处理流程:

1)主线程通过execute()方法向线程池中提交任务:

  • 线程池中的线程数小于核心线程数,继续向线程池中提交任务时,创建线程执行任务处理。

  • 线程池中的线程数大于等于核心线程数:

    a) 如果阻塞队列未满,继续向线程池中提交任务时,将任务添加到阻塞队列中,等待处理。

    b) 如果阻塞队列已满且线程数小于最大线程数,继续向线程池中提交任务时,创建线程执行任务处理。

    c) 如果阻塞队列已满且线程数大于等于最大线程数,继续向线程池中提交任务时,执行拒绝策略。

执行任务

任务提交到线程池中后,会执行工作线程(Worker)的ThreadPoolExecutor.Worker#run方法执行任务处理,而run()方法中会调用ThreadPoolExecutor#runWorker方法。

ThreadPoolExecutor#runWorker方法中,通过自旋的方式从Worker工作线程或阻塞队列中获取任务进行处理:

  • 如果Worker工作线程中存在任务,则执行Worker工作线程中的任务。
  • 如果Worker工作线程中不存在任务,则通过getTask()方法从获取任务,并执行该任务。
  • 如果Worker工作线程和阻塞队列中没有可执行的任务,则退出并销毁Worker工作线程。

线程池工作流程

线程池工作流程,如图所示:

线程池工作流程

处理流程:

1)向线程池中提交任务,判断线程池中线程数是否小于核心线程数:

  • 线程数小于核心线程数,则创建线程执行任务处理。

  • 线程数大于等于核心线程数,则判断线程池中阻塞队列是否已满:

    a) 阻塞队列未满,则将任务添加到阻塞队列中,等待执行。

    b) 阻塞队列已满,则判断线程池中的线程数是否小于最大线程数:

    线程数小于最大线程数,则创建线程执行任务处理。

    线程数大于等于最大线程数,则执行拒绝策略。

线程池创建方式

线程池的创建方式一般分为两种:

  • 通过Executors类创建线程池。
  • 通过ThreadPoolExecutor类创建线程池。

通过Executors类创建线程池

Executors类是JDK提供的一个创建线程池的工具类,内部通过调用ThreadPoolExecutor构造函数来实现,通过该类提供的静态方法可以快速创建一些常用线程池。

newFixedThreadPool

创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。

静态方法:

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

其中:

  • 核心线程数为初始化时指定的线程数,核心线程数等于最大线程数。
  • 线程存活时间keepAliveTime为0,表示线程空闲时立刻被回收。
  • 阻塞队列为LinkedBlockingQueue(默认值)。

newCachedThreadPool

创建一个可缓存的线程池,该线程池的大小为Integer.MAX_VALUE,对于提交的新任务:

  • 如果存在空闲线程,则使用空闲线程来执行任务处理。
  • 如果不存在空闲线程,则新建一个线程来执行任务处理。
  • 如果线程池中的线程数超过处理任务需要的线程数,则空闲线程缓存一段时间(60s)后会被回收。

静态方法:

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

其中:

  • 核心线程数为0。
  • 最大线程数为Integer.MAX_VALUE。
  • 线程存活时间keepAliveTime为60s。
  • 阻塞队列为SynchronousQueue(同步队列)。

newSingleThreadExecutor

创建一个单线程化的线程池,它只有一个工作线程来执行任务,所有任务按照先进先出的顺序执行。

静态方法:

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

其中:

  • 核心线程数为1。
  • 最大线程数为1。
  • 线程存活时间keepAliveTime为0,表示线程空闲时立刻被回收。
  • 阻塞队列为LinkedBlockingQueue。

newScheduledThreadPool

创建一个计划线程池,支持定时或周期性的执行任务(如:延时任务)。

静态方法:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

其中:

  • 核心线程数为初始化时指定的线程数。
  • 最大线程数为Integer.MAX_VALUE。
  • 线程存活时间keepAliveTime为0,表示线程空闲时立刻被回收。
  • 阻塞队列为DelayedWorkQueue(延时队列)。

其他线程池

Executors工具类除了能快速创建以上的常用线程池外,还可以创建很多其他线程池。如:

  • Executors.newSingleThreadScheduledExecutor:创建一个单线程化的计划线程池(newScheduledThreadPool的单线程版本)。
  • Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定),该线程池JDK1.8添加。

代码示例:

ExecutorsExample.java

@Slf4j
public class ExecutorsExample {
    /**
     * 创建一个固定大小的线程池
     */
    private static ExecutorService executor = Executors.newFixedThreadPool(10);
    public static void main(String[] args) {
        for (int i = 0; i < 10000; i++) {
            // 在线程池中执行任务
            executor.execute(new Task());
        }
    }
    /**
     * 任务线程
     */
    static class Task implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                log.info("执行线程:{}", Thread.currentThread().getName());
            } catch (InterruptedException e) {
                log.info("线程中断异常");
            }
        }
    }
}

不推荐使用Executors类创建线程池,主要原因:Executors类创建线程池默认使用LinkedBlockingQueue,LinkedBlockingQueue默认大小为Integer.MAX_VALUE(相当于无界队列),在高负载情况下很容易导致OOM。因此,强烈建议使用有界队列创建线程池。

通过ThreadPoolExecutor类创建线程池

通过ThreadPoolExecutor类手动创建线程池(推荐方式)。

代码示例:

ThreadPoolExecutorExample.java

@Slf4j
public class ThreadPoolExecutorExample {
    /**
     * 定义线程池
     */
    private static ExecutorService pool ;
    public static void main(String[] args) {
        // 创建线程池
        pool = new ThreadPoolExecutor(10, 20, 0,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(5),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
        // 在线程池中执行任务
        for (int i = 0; i < 10000; i++) {
            pool.execute(new ThreadPoolExecutorExample.Task());
        }
    }
    /**
     * 任务线程
     */
    static class Task implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                log.info("执行线程:{}", Thread.currentThread().getName());
            } catch (InterruptedException e) {
                log.info("线程中断异常");
            }
        }
    }
}

其他推荐方式:通过commons-lang3、com.google.guava等工具包创建线程池,创建示例自行查阅相关资料即可。

线程池大小设置

在并发编程领域,提升性能本质上就是提升硬件的利用率(即:提升CPU和I/O设备综合利用率)的问题。因此,线程池大小需要根据CPU密集型和I/O密集型场景进行设置:

  • 对于CPU密集型场景,线程池大小一般设置为:CPU核数 + 1。
  • 对于I/O密集型场景,线程池大小一般设置为:CPU核数 * [1 +(I/O耗时 / CPU耗时)]。

以上公式仅作为参考值,具体情况需要根据压测情况进行设置。

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

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

相关文章

书生·浦语大模型实战营 | 第2次学习笔记

前言 书生浦语大模型应用实战营 第二期正在开营&#xff0c;欢迎大家来学习。&#xff08;参与链接&#xff1a;课程升级&#xff0c;算力免费&#xff0c;书生浦语实战营第二期学员招募&#xff5c;活动预告https://mp.weixin.qq.com/s/YYSr3re6IduLJCAh-jgZqg&#xff09; …

蓝桥杯:七步诗 ← bfs

【题目来源】https://www.lanqiao.cn/problems/3447/learning/【题目描述】 煮豆燃豆苴&#xff0c;豆在釜中泣。本是同根生&#xff0c;相煎何太急?---曹植 所以&#xff0c;这道题目关乎豆子! 话说赤壁之战结束后&#xff0c;曹操的船舰被刘备烧了&#xff0c;引领军队从华容…

域名如何端口映射?

域名端口映射是一种重要的网络技术&#xff0c;它可以实现不同设备之间的远程通信。在全球互联网的背景下&#xff0c;人们之间的通信变得非常便捷&#xff0c;但随之而来的问题是如何有效地实现设备之间的互联互通。域名端口映射正是为了解决这个问题而出现的。 天联组网 天联…

单片机之LED与按键

目录 LED LED灯亮的原理图 LED灯光闪烁 电路设计 keil文件 LED流水灯的实现 keil文件 单片机之按键 键盘的结构 按键消抖 软件消抖 硬件消抖 键盘的分类 独立式键盘 行列式键盘 键盘的识别 独立按键案例 电路图 keil文件 行列式键盘案例 电路图 对应按键…

该主机与 Cloudera Manager Server 失去联系的时间过长。 该主机未与 Host Monitor 建立联系

该主机与 Cloudera Manager Server 失去联系的时间过长。 该主机未与 Host Monitor 建立联系 这个去集群主机cm界面上看会出现这个错误 排查思路&#xff1a; 一般比较常见的原因可能是出问题的主机和集群主节点的时间对应不上了。还有就是cm agent服务出现问题了 去该主机的…

开源模型应用落地-chatglm3-6b模型小试-入门篇(三)

一、前言 刚开始接触AI时&#xff0c;您可能会感到困惑&#xff0c;因为面对众多开源模型的选择&#xff0c;不知道应该选择哪个模型&#xff0c;也不知道如何调用最基本的模型。但是不用担心&#xff0c;我将陪伴您一起逐步入门&#xff0c;解决这些问题。 在信息时代&#xf…

安卓开机动画

目录 一、开机动画的2种模式1.1 android模式1.2 movie模式 二、开机动画代码运行位置三、删除开机动画四、自定义开机动画实践 一、开机动画的2种模式 一种是使用两张图片利用某种效果来造成动态&#xff0c;另一种则是用一个图包循环显示的方式来形成动态。当然&#xff0c;这…

JAVA基础03-scanner,输出,循环,if的使用以及eclipse的安装

目录 scanner的使用 if语句的使用 eclipse的使用 switch语句的使用 输出方法的使用 循环语句 scanner的使用 实现用户数据的交互&#xff0c;用户通过终端输入数据 注意&#xff1a;使用Scanner需要导包 在程序开头加上&#xff1a;import java.util.Scanner; //由于S…

【设计原则】CQRS

文章目录 概述组成与特点优缺点何时使用 CQRS 模式推荐阅读 概述 CQRS&#xff08;Command Query Responsibility Segregation&#xff09;是一种软件设计模式&#xff0c;其核心设计理念是将一个对象的数据访问&#xff08;查询&#xff09;和数据操作&#xff08;命令&#…

QT----YOLOv5检测平台

目录 1 opencv环境安装1.1 报错Could NOT find CUDNN (missing: CUDNN_LIBRARY CUDNN_INCLUDE_DIR) (Required is at least version "7.5")1.2 使用camkevs编译opencv4.8.01.3 报错operator !":重载函数具有类似的转换(编译源文件 H:\opencv-4.8.0\opencv-4.8.0…

PAC性能开销权衡及优化措施

PAC性能开销&#xff1f;如何进行优化&#xff1f;本博客探讨这些问题。

[StartingPoint][Tier0]Mongod

Task 1 How many TCP ports are open on the machine? (机器上打开了多少个 TCP 端口&#xff1f;) Example: $ sudo nmap -sS -T4 10.129.222.112 -p 27017,22 2 Task 2 Which service is running on port 27017 of the remote host? (哪个服务正在远程主机的端口 270…

NASA数据集——1980 年至 2020 年北美 3km分辨率气温(摄氏度)、相对湿度(%)、风速(米/秒)、风向(真北偏角)、总降水量(雨+雪)等数据集

Daily SnowModel Outputs Covering the ABoVE Core Domain, 3-km Resolution, 1980-2020 简介 文件修订日期&#xff1a;2023-01-27 数据集版本: 1 摘要 该数据集提供了 1980 年 9 月 1 日至 2020 年 8 月 31 日期间 3 千米网格上的 SnowModel 每日模拟输出&#xff0c;涵…

Java快速入门系列-3(Java基础)

第三章&#xff1a;Java基础 3.1 Java语法基础3.1.1 Java程序入口点&#xff1a;main方法3.1.2 注释3.1.3 变量声明与赋值3.1.4 数据类型3.1.5 标识符与关键字 3.2 数据类型与变量3.2.1 基本数据类型3.2.2 引用数据类型 3.3 控制流程3.3.1 条件语句3.3.2 循环结构 3.4 数组与集…

mbti,ESTP型人格的心理问题分析

什么是ESTP型人格 ESTP分别代表外向&#xff0c;实感&#xff0c;理智&#xff0c;依赖&#xff0c;而ESTP型人格则是一种性格上十分激进&#xff0c;喜欢冒险&#xff0c;并且总是因为情绪起伏过大&#xff0c;而一下子做出应激行为的相对冒险的人格。具有ESTP型人格的人一般…

页面刚加载的时候显示自己定义的{{***}}然后一闪而过

这时候别用插值表达式语法了&#xff0c;直接用v-text或者v-html就能解决这个问题 但是有个问题&#xff0c;如下图所示&#xff1a; 具体bind使用方式&#xff0c;如下图所示&#xff1a; 但是v-bind也可以进行简写&#xff0c;就是去掉v-bind&#xff0c;直接写&#xff1a…

提高空调压缩机能效的通用方法

压缩机的能效提高主要依靠技术改进而不是大幅度增加材料的消耗&#xff0c;这也是技术经济性最好的节能手段。 1、改进电机效率&#xff0c;电机效率的提高意味着压缩机电效率的提高和压缩机总体效率的提高&#xff1b; 1.1、降低定子铜耗 降低定子绕组中电流通过所产生的铜耗…

前端路径问题总结

1.相对路径 不以/开头 以当前资源的所在路径为出发点去找目标资源 语法: ./表示当前资源的路径 ../表示当前资源的上一层路径 缺点:不同位置,相对路径写法不同2.绝对路径 以固定的路径作为出发点作为目标资源,和当前资源所在路径没关系 语法:以/开头,不同的项目中,固定的路径…

Java零基础入门-java8新特性(完结篇)

一、概述 ​上几期&#xff0c;我们是完整的学完了java异常类的学习及实战演示、以及学习了线程进程等基础概念&#xff0c;而这一期&#xff0c;我们要来玩点好的东西&#xff0c;那就是java8&#xff0c;我们都知道java8是自2004年发布java5之后最重要且一次重大的版本更新&a…

4月4号总结

java学习 一.接口 1.介绍 定义接口需要使用到关键字interface去定义接口。 格式如下&#xff1a; 类与接口的关系不是继承&#xff0c;而是实现&#xff0c;用关键字 implements &#xff0c;格式如下&#xff1a; 这个类去实现接口&#xff0c;其中的关系就相当于&#xf…