学习JavaEE的日子 Day32 线程池

Day32 线程池

1.引入

一个线程完成一项任务所需时间为:

  1. 创建线程时间 - Time1
  2. 线程中执行任务的时间 - Time2
  3. 销毁线程时间 - Time3

2.为什么需要线程池(重要)

  1. 线程池技术正是关注如何缩短或调整Time1和Time3的时间,从而提高程序的性能。项目中可以把Time1,T3分别安排在项目的启动和结束的时间段或者一些空闲的时间段
  2. 线程池不仅调整Time1,Time3产生的时间段,而且它还显著减少了创建线程的数目,提高线程的复用率
  3. 系统启动一个新线程的成本是比较高的,因为涉及与操作系统的交互,在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,优先考虑使用线程池

3.Java提供的线程池(了解即可)

一般不会使用这个,局限性太多

ExecutorService:线程池的接口

Executors:创建各种线程池的工具类

public class Test {public static void main(String[] args) {//创建单个线程的线程池//ExecutorService pool = Executors.newSingleThreadExecutor();//创建指定线程的线程池//ExecutorService pool = Executors.newFixedThreadPool(3);//创建可缓存线程的线程池,自动回收60s闲置线程ExecutorService pool = Executors.newCachedThreadPool();//循环创建任务对象,并提交给线程池for (int i = 1; i <= 100; i++) {//创建任务对象Task task = new Task(i);//提交任务pool.execute(task);}//关闭线程池pool.shutdown();}
}class Task implements Runnable{private int i;public Task(int i) {this.i = i;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "处理了第" + num + "个任务");}
}

4.深入源码

ExecutorService pool = Executors.newSingleThreadExecutor();

ExecutorService pool = Executors.newFixedThreadPool(3);

ExecutorService pool = Executors.newCachedThreadPool();

三种线程池底层都是ThreadPoolExecutor类的对象

-- 分析ThreadPoolExecutor类的构造方法源码--------------------------------
public ThreadPoolExecutor(int corePoolSize,		------------- 核心线程数量 int maximumPoolSize,	------------- 最大线程数量 long keepAliveTime,		------------- 闲置时间,作用于核心线程数与最大线程数之间的线程TimeUnit unit,			------------- keepAliveTime的时间单位(可以是毫秒、秒....)BlockingQueue<Runnable> workQueue, -- 任务队列ThreadFactory threadFactory, -------- 线程工厂RejectedExecutionHandler handler ---- 达到了线程界限和队列容量时的处理方案(拒绝策略)
) {}
执行步骤:1.创建线程池后2.任务提交后,查看是否有核心线程:3.1 没有 -> 就创建核心线程 -> 执行任务 -> 执行完毕后又回到线程池中3.2-> 查看是否有闲置核心线程:4.1-> 执行任务 -> 执行完毕后又回到线程池4.2 没有 -> 查看当前核心线程数是否核心线程数量:5.1-> 就创建核心线程 -> 执行任务 -> 执行完毕后又回到线程池中5.2-> 查看任务列表是否装载满:6.1 没有 -> 就放入列表中,等待出现闲置线程6.2 装满 -> 查看是否有普通线程(核心线程数到最大线程数量之间的线程)7.1 没有 -> 就创建普通线程 -> 执行任务 -> 执行完毕后又回到线程池中7.2-> 查看是否有闲置普通线程7.1.1-> 执行任务 -> 执行完毕后又回到线程池中7.1.2 没有 -> 查看现在所有线程数量是否为最大线程数:8.1-> 执行处理方案(默认处理抛出异常)8.2->就创建普通线程-> 执行任务 -> 执行完毕后又回到线程池中     			
注:1.为了更好的理解,在这里区分核心线程和普通线程,实际上区分的这么清楚,都是线程2.默认的处理方案就是抛出RejectedExecutionException-- 分析单个线程的线程池的源码 --------------------------------
ExecutorService pool = Executors.newSingleThreadExecutor();
new ThreadPoolExecutor(1, -- 核心线程数量 1, -- 最大线程数量 0L, -- 闲置时间TimeUnit.MILLISECONDS, -- 时间单位(毫秒)new LinkedBlockingQueue<Runnable>() -- 无界任务队列,可以无限添加任务(内存溢出问题)
)  
-- 分析指定线程的线程池的源码 --------------------------------
ExecutorService pool = Executors.newFixedThreadPool(3);
new ThreadPoolExecutor(nThreads, -- 核心线程数量 nThreads, -- 最大线程数量 0L, -- 闲置时间TimeUnit.MILLISECONDS, -- 时间单位(毫秒)new LinkedBlockingQueue<Runnable>()-- 无界任务队列,可以无限添加任务(内存溢出问题)
)
-- 创建可缓存线程的线程池 -----------------------------------
new ThreadPoolExecutor(0, -- 核心线程数量 Integer.MAX_VALUE,-- 最大线程数量 60L, -- 闲置时间TimeUnit.SECONDS, -- 时间单位()new SynchronousQueue<Runnable>() -- 直接提交队列(同步队列):没有容量队列(最大线程数量是21亿多,太大了)

总结:核心线程满载 -> 任务队列 -> 普通线程 -> 拒绝策略

在这里插入图片描述

5.任务队列详解

队列名称详解
LinkedBlockingQueue无界任务队列使用无界任务队列,线程池的任务队列可以无限制的添加新的任务,而线程池创建的最大线程数量就是你corePoolSize设置的数量,也就是说在这种情况下maximumPoolSize这个参数是无效的,哪怕你的任务队列中缓存了很多未执行的任务,当线程池的线程数达到corePoolSize后,就不会再增加了;若后续有新的任务加入,则直接进入队列等待,当使用这种任务队列模式时,一定要注意你任务提交与处理之间的协调与控制,不然会出现队列中的任务由于无法及时处理导致一直增长,直到最后资源耗尽的问题
SynchronousQueue 同步任务队列 直接提交任务队列使用直接提交任务队列,队列没有容量,每执行一个插入操作就会阻塞,需要再执行一个删除操作才会被唤醒,反之每一个删除操作也都要等待对应的插入操作。 任务队列为SynchronousQueue,创建的线程数大于maximumPoolSize时,直接执行了拒绝策略抛出异常。 使用SynchronousQueue队列,提交的任务不会被保存,总是会马上提交执行。如果用于执行任务的线程数量小于maximumPoolSize,则尝试创建新的线程,如果达到maximumPoolSize设置的最大值,则根据你设置的handler执行拒绝策略。因此这种方式你提交的任务不会被缓存起来,而是会被马上执行,在这种情况下,你需要对你程序的并发量有个准确的评估,才能设置合适的maximumPoolSize数量,否则很容易就会执行拒绝策略;
ArrayBlockingQueue有界任务队列使用有界任务队列,若有新的任务需要执行时,线程池会创建新的线程,直到创建的线程数量达到corePoolSize时,则会将新的任务加入到等待队列中。若等待队列已满,即超过ArrayBlockingQueue初始化的容量,则继续创建线程,直到线程数量达到maximumPoolSize设置的最大线程数量,若大于maximumPoolSize,则执行拒绝策略。在这种情况下,线程数量的上限与有界任务队列的状态有直接关系,如果有界队列初始容量较大或者没有达到超负荷的状态,线程数将一直维持在corePoolSize以下,反之当任务队列已满时,则会以maximumPoolSize为最大线程数上限。
PriorityBlockingQueue优先任务队列使用优先任务队列,它其实是一个特殊的无界队列,它其中无论添加了多少个任务,线程池创建的线程数也不会超过corePoolSize的数量,只不过其他队列一般是按照先进先出的规则处理任务,而PriorityBlockingQueue队列可以自定义规则根据任务的优先级顺序先后执行。

对优先队列的使用说明:

除了第一个任务直接创建线程执行外,其他的任务都被放入了优先任务队列,按优先级进行了重新排列执行,且线程池的线程数一直为corePoolSize,也就是只有一个。

5.1 无界队列

理解:这个队列没有上线

继承关系:LinkedBlockingQueue -> AbstractQueue -> AbstractCollection

小结:

1.LinkedBlockingQueue是Collection集合家族的一员

2.Collection集合家族(List、Set、Queue)

3.LinkedBlockingQueue数据结构单向链表

缺点:LinkedBlockingQueue可能造成内存溢出

public class Test01 {public static void main(String[] args) throws InterruptedException {//创建无界队列LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();//添加元素queue.put("aaa");queue.put("bbb");queue.put("ccc");queue.put("ddd");queue.put("eee");queue.put("fff");//删除元素queue.remove("ccc");//遍历队列Iterator<String> it = queue.iterator();while (it.hasNext()) {String string = it.next();System.out.println(string);}}
}
public class Task implements Runnable,Comparable<Task>{private int priority;//优先级别public Task(int priority) {this.priority = priority;}@Overridepublic void run() {System.out.println("任务被处理了");}//当前对象和其他对象做比较,当前优先级大就返回-1,优先级小就返回1,值越小优先级越高@Overridepublic int compareTo(Task o) {return Integer.compare(this.priority, o.priority);}public int getPriority() {return priority;}
}
5.2 有界队列

继承关系:ArrayBlockingQueue -> AbstractQueue -> AbstractCollection

小结:

1.ArrayBlockingQueue数据结构一维数组

缺点:不能有效控制项目中的线程目数

public class Test02 {public static void main(String[] args) throws InterruptedException {//创建有界队列ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(6);//添加元素queue.put("aaa");queue.put("bbb");queue.put("ccc");queue.put("ddd");queue.put("eee");queue.put("fff");//删除元素queue.remove("ccc");//遍历队列Iterator<String> it = queue.iterator();while (it.hasNext()) {String string = it.next();System.out.println(string);}}
}
5.3 优先队列

继承关系:ArrayBlockingQueue -> AbstractQueue -> AbstractCollection

ArrayBlockingQueue数据结构一维数组

public class Test03 {public static void main(String[] args) throws InterruptedException {//创建优先队列PriorityBlockingQueue<Task> queue = new PriorityBlockingQueue<>();//无参构造底层使用的是元素的内置比较器//添加元素queue.add(new Task(3));queue.add(new Task(1));queue.add(new Task(4));queue.add(new Task(2));//遍历取出队列while(!queue.isEmpty()){//删除第一元素Task poll = queue.poll();System.out.println(poll.getPriority());}		}
}

6.拒绝策略

ThreadPoolExecutor自带的拒绝策略有四种,都实现了RejectedExecutionHandler接口

比如:new ThreadPoolExecutor.AbortPolicy()

拒绝策略解释
AbortPolicy当有任务添加到线程池被拒绝时,会抛出RejectedExecutionException异常 线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。
DiscardPolicy当有任务添加到线程池被拒绝时,直接丢弃,其他啥都没有
CallerRunsPolicy当有任务添加到线程池被拒绝时,线程池会将被拒绝的任务添加到线程池正在运行的线程中去运行。 一一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大
DiscardOledestPolicy当有任务添加到线程池被拒绝时,线程池会丢弃阻塞队列中末尾的任务(最老的任务),然后将被拒绝的任务添加到末尾。 如果项目中有允许丢失任务的需求,可以使用
6.1 自定义拒绝策略(实现RejectedExecutionHandler接口)
public class Test {public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new RejectedExecutionHandler() {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println(r.toString()+"执行了拒绝策略");}});for (int i = 1; i <= 10; i++) {pool.execute(new Task());}pool.shutdown();}
}
class Task implements Runnable{@Overridepublic void run() {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}
}

7.自定义线程池原因(重要)

为什么不使用java自带的线程池?而去自定义线程池?

在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面使线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活,而且有资源耗尽的风险(OOM - Out Of Memory )。

一般我们创建线程池时,为防止资源被耗尽,任务队列都会选择创建有界任务队列,但这种模式下如果出现任务队列已满且线程池创建的线程数达到你设置的最大线程数时,这时就需要你指定ThreadPoolExecutor的RejectedExecutionHandler参数即合理的拒绝策略,来处理线程池"超载"的情况

7.1 自定义线程池

七大属性要记住

前提学习线程池中如何创建线程:

线程池中线程就是通过ThreadPoolExecutor中的ThreadFactory,线程工厂创建的。那么通过自定义ThreadFactory,可以按需要对线程池中创建的线程进行一些特殊的设置,如命名、优先级等

public class Test01 {public static void main(String[] args) {ThreadPoolExecutor pool = new ThreadPoolExecutor(5, //核心线程数20, //最大线程数60, //闲置时间TimeUnit.SECONDS, //时间单位new ArrayBlockingQueue<>(30), //任务队列 -- 有界队列new ThreadFactory() {//自定义线程工厂private int num = 1;@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);t.setName("自定义线程池中的线程"+num);t.setPriority(Thread.MAX_PRIORITY);num++;return t;}}, new RejectedExecutionHandler() {//自定义拒绝策略@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("任务被拒绝了~~~");}});for (int i = 1; i <= 100; i++) {Task task = new Task(i);pool.execute(task);}pool.shutdown();}
}
public class Task implements Runnable{private int num;public Task(int num) {this.num = num;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "处理了第" + num + "个任务");}}
7.2 自己写一个线程池

自定义线程池类

//自己写的线程池
public class FastThreadPool extends ThreadPoolExecutor{public FastThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new FastThreadFactory(), new FastRejectedExecutionHandler());}public FastThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);}//七大属性记住,重要!!!@Overrideprotected void beforeExecute(Thread t, Runnable r) {System.out.println("线程执行任务之前调用 -- " + r);}@Overrideprotected void afterExecute(Runnable r, Throwable t) {System.out.println("线程执行任务之后调用 -- " + r);}@Overrideprotected void terminated() {System.out.println("线程池关闭时调用");}//自定义线程工厂(实现ThreadFactory接口)private static class FastThreadFactory implements ThreadFactory{private int num;//线程编号@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);t.setName("自定义线程池中的线程"+num);t.setPriority(Thread.MAX_PRIORITY);num++;return t;}}//自定义拒绝策略(实现RejectedExecutionHandler)private static class FastRejectedExecutionHandler implements RejectedExecutionHandler{@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("任务被拒绝了~~~");}}}

任务类

public class Task implements Runnable{private int num;public Task(int num) {this.num = num;}@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + "处理了第" + num + "个任务");}@Overridepublic String toString() {return "任务" + num;}
}

测试类

public class Test01 {public static void main(String[] args) {FastThreadPool pool = new FastThreadPool(5, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(30));for (int i = 1; i <= 100; i++) {Task task = new Task(i);pool.execute(task);}//设置为true后,闲置时间一旦到达,核心线程也会被销毁//经验:我们一般不会回收核心线程,因为设置回收后线程池中的线程有可能为0,这样就没有线程复用率的说法了//pool.allowCoreThreadTimeOut(true);pool.shutdown();}
}

面试题:线程池中核心线程会被回收吗?

当线程池调用了allowCoreThreadTimeOut(true);时,核心线程会被回收

但是一般不会调用该方法,也就是说,项目中线程池中的核心线程不要设计被回收,因为如果线程池中线程全部都回收了,就没有线程复用率这个说法

核心线程宁可闲置也不回收

7.3 ThreadPoolExecutor扩展

ThreadPoolExecutor扩展主要是围绕beforeExecute()、afterExecute()和terminated()三个方法的重写, 通过这三个方法我们可以监控每个任务的开始和结束时间,或者其他一些功能。下面我们可以通过代码实现一下

方法解释
beforeExecute线程池中任务运行前执行
afterExecute线程池中任务运行完毕后执行
terminated线程池退出后执行

上面自己写的自定义线程池里有相关使用

8.线程池线程数量

实际工作中使用 sysbench多线程性能测试工具 (重要),因为七大属性中的核心线程数,最大线程数,闲置时间不能随便乱写一个数字上去,而是要进行相关测试才能得出数据

9.带有返回值的任务类(实现 Callable<>接口)

注意:带有返回值的任务类和线程池一起使用

需求:计算任务,一个包含了2万个整数的数组,分拆了多个线程来进行并行计算,最后汇总出计算的结果。

public class Test01 {public static void main(String[] args) throws InterruptedException, ExecutionException {//创建数组int[] arr = new int[20000];//初始化数组数据 -- {1,2,3,....,20000}for (int i = 0; i < arr.length; i++) {arr[i] = i+1;}//创建线程池FastThreadPool pool = new FastThreadPool(4, 4, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5));//创建任务Task task1 = new Task(arr, 0, 5000,1);Task task2 = new Task(arr, 5000, 10000,2);Task task3 = new Task(arr, 10000, 15000,3);Task task4 = new Task(arr, 15000, 20000,4);//提交任务,任务完成后会返回Future对象,Future对象里存储了任务的返回值数据Future<Integer> future1 = pool.submit(task1);Future<Integer> future2 = pool.submit(task2);Future<Integer> future3 = pool.submit(task3);Future<Integer> future4 = pool.submit(task4);//合并任务返回值System.out.println(future1.get() + future2.get() + future3.get() + future4.get());pool.shutdown();}
}

带返回值的任务类

public class Task implements Callable<Integer>{private int[] arr;private int startIndex;private int endIndex;private int num;public Task(int[] arr, int startIndex, int endIndex,int num) {this.arr = arr;this.startIndex = startIndex;this.endIndex = endIndex;this.num = num;}//理解:线程抢到CPU资源后,才会调用call方法,call方法相当于Runnable接口中的run方法@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = startIndex; i < endIndex; i++) {sum += arr[i];System.out.println("任务" + num);}return sum;}}

总结

1.Java自带的线程池
单个线程的线程池
指定线程个数的线程池
可缓存的线程池
延迟任务的线程池

2.线程池的7大参数
核心线程数
最大线程数
任务队列(有界、无界、同步、优先队列)
拒绝策略
闲置时间
时间单位
线程工厂

3.线程池的调用步骤(核心线程、任务队列、普通线程、拒绝策略)

4.任务队列及底层原理(有界、无界、同步、优先队列)

5.自定义线程池
自定义线程工厂
自定义拒绝策略

6.带有返回值的任务类 --Callable

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

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

相关文章

【 MyBatis 】| 关于多表联查返回 List 集合只查到一条的 BUG

目录 一. &#x1f981; 写在前面二. &#x1f981; 探索过程2.1 开端 —— 开始写 bug2.2 发展 —— bug 完成2.3 高潮 —— bug探究2.4 结局 —— 效果展示 三. &#x1f981; 写在最后 一. &#x1f981; 写在前面 今天又是 BUG 气满满的一天&#xff0c;一个 xxxMapper.xm…

跑spark的yarn模式时RM连不上的情况

在linux控制台跑spark on yarn一个测试案例&#xff0c;日志中总显示RM连yarn服务的时候是&#xff1a;0.0.0.0:8032 具体情况如下图&#xff1a; 我问题出现的原因&#xff0c;总结如下&#xff1a; 1.防火墙没关闭&#xff0c;关闭 2.spark-env.sh这个文件的YARN_CONF_DIR…

MyBatis基础使用

MyBatis首页https://mybatis.net.cn/ MyBatis细节注意&#xff0c;让你更加熟悉MyBatishttps://blog.csdn.net/m0_61160520/article/details/137173558?spm1001.2014.3001.5501 1.项目目录 2.数据库 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_e…

Linux文件与进程交互的窥探者lsof

lsof 是一个 Linux 和 UNIX 系统中的实用工具,用于列出系统中打开文件的所有信息。这个名字代表 “List Open Files”,但它也可以显示进程相关的其他信息,如: 打开的文件描述符列表 打开网络连接的列表 被进程使用的信号和内核对象等 在Linux系统中,有一个经典的概念: …

vue3+threejs新手从零开发卡牌游戏(二十):添加卡牌被破坏进入墓地逻辑

在game目录下新建graveyard文件夹存放墓地相关代码&#xff1a; game/graveyard/p1.vue&#xff0c;这里主要设置了墓地group的位置&#xff1a; <template><div></div> </template><script setup lang"ts"> import { reactive, ref,…

【刷题】 二分查找入门

送给大家一句话: 总有一天&#xff0c;你会站在最亮的地方&#xff0c;活成自己曾经渴望的模样—— 苑子文 & 苑子豪《我们都一样 年轻又彷徨》 二分查找入门 1 前言2 Leetcode 704. 二分查找2.1 题目描述2.2 算法思路 3 Leetcode 34. 在排序数组中查找元素的第一个和最后…

求组合背包II(acwing)

题目描述&#xff1a; 给定n组循问&#xff0c;每组询问给定两个整数a&#xff0c;b&#xff0c;请你输出Ca^b mod (1e9 7)的值&#xff0c;。 输入格式&#xff1a; 第一行包含整数n。 接下来2行&#xff0c;每行包含一组a和b。 输出格式&#xff1a; …

Leetcode 4.1

LeetCode 热题 100 贪心算法1.买卖股票的最佳时机2.跳跃游戏3.跳跃游戏 II4.划分字母区间 区间合并1.合并区间 贪心算法 1.买卖股票的最佳时机 买卖股票的最佳时机 买的那天一定是卖的那天之前的最小值。 每到一天&#xff0c;维护那天之前的最小值即可。 在题目中&#xff0…

面试题:MySQL 优化篇

定位慢查询 &#x1f496; 开源工具 调试工具&#xff1a;Arthas&#xff08;阿尔萨斯&#xff09;运维工具&#xff1a;Prometheus&#xff08;普罗米修斯&#xff09;、Skywalking &#x1f496; MySQL 慢查询日志 # 开启 MySQL 慢查询日志开关 slow_query_log1 # 设置慢…

微信小程序wx.navigateTo无法跳转到Component组件问题解决。(共享元素动画必备)

关于Component构造器官方是有文档说明的&#xff0c;然后官方文档内部也给出了组件是可以通过像pages一样跳转的。但是官方文档缺少了必要的说明&#xff0c;会引起wx.navigateTo无法跳转到组件问题&#xff01; 扫码查看小程序&#xff0c;可以体验效果&#xff1a; 以下是官方…

ElementUI表格table组件实现单选及禁用默认选中效果

在使用ElementUI&#xff0c;需要ElementUI表格table组件实现单选及禁用默认选中效果, 先看下效果图&#xff1a; 代码如下&#xff1a; <template><el-tableref"multipleTable":data"tableData"tooltip-effect"dark"style"widt…

C语言之位段

1.位段的声明 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1.位段的成员必须是 int、unsigned int 或signed int 。 2.位段的成员名后边有一个冒号和一个数字。 比如&#xff1a; struct A {int _a:2;int _b:5;int _c:10;int _d:30; }; A 就是一个位段类型…

vue3 渲染一个后端返回的图片字段渲染、table表格内放置图片

一、后端直接返回图片url 当图片字段接口直接返回的是图片url&#xff0c;可以直接放到img标签上 <img v-if"thumbLoader" class"r-image-loader-thumb" :src"resUrl" /> 二、当图片字段接口直接返回的是图片Id 那么就需要去拼一下图片…

商品服务 - 三级分类

1.递归查询树形结构 Overridepublic List<CategoryEntity> listWithTree() {//1.查出所有分类List<CategoryEntity> all this.list();//2.组装成父子的属性结构List<CategoryEntity> level1Menus all.stream().filter(c -> c.getParentCid().equals(0L)…

LeetCode-560. 和为 K 的子数组【数组 哈希表 前缀和】

LeetCode-560. 和为 K 的子数组【数组 哈希表 前缀和】 题目描述&#xff1a;解题思路一&#xff1a;一边算前缀和一边统计。这里用哈希表统计前缀和出现的次数&#xff0c;那么和为k的子数组的个数就是当前前缀和-k的个数&#xff0c;即preSums[presum - k]。画个图表述就是&a…

FPGA之状态机学习

作为一名逻辑工程师&#xff0c;掌握和应用状态机设计是必不可少的。能够灵活的应用状态机是对逻辑工程师最基本的要求&#xff0c;状态机设计的好坏能够直接影响到设计系统的稳定性&#xff0c;所以学会状态机是非常的重要。 1.状态机的概念 状态机通过不同的状态迁移来完成特…

Java封装最佳实践:打造高内聚、低耦合的优雅代码~

​ 个人主页&#xff1a;秋风起&#xff0c;再归来~ 文章专栏&#xff1a;javaSE的修炼之路 个人格言&#xff1a;悟已往之不谏&#xff0c;知来者犹可追 克心守己&#xff0c;律己则安&#xff01; 1、封装 1.1 封装的概念 面向对象程序三大…

从0到1利用express搭建后端服务

目录 1 架构的选择2 环境搭建3 安装express4 创建启动文件5 express的核心功能6 加入日志记录功能7 日志记录的好处本节代码总结 不知不觉学习低代码已经进入第四个年头了&#xff0c;既然低代码很好&#xff0c;为什么突然又自己架构起后端了呢&#xff1f;我有一句话叫低代码…

【数据结构】优先级队列——堆

&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;个人主页&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388; &#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;数据结构专栏&#x1f388;&#x1f388;&#x1f388;&…

数码管时钟--LABVIEW编程

一、程序的前面板 1.获取系统时钟&#xff0c;年月日&#xff0c;时分秒&#xff0c;用14个数码管显示。 2.闹钟设定小时和分钟。 二、程序的后面板 三、程序运行图 四、程序源码 源程序可以在百度网盘自行下载&#xff0c;地址链接见下方。 链接&#xff1a;https://pan.b…