Java-多线程2
-
什么是线程?
线程是 cpu调度和执行的单位。多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。
如何实现线程
继承Thread类
实现步骤:
- 创建自定义类,继承Thread类
- 重写run方法
- 创建自定义类对象,并.start()启动线程
class Th extends Thread {@Overridepublic void run() {for (int i = 0; i < 2000; i++) {System.out.println(i + "Th");}}public static void main(String[] args) {Th th = new Th();th.start();} }
实现Runnable接口
实现步骤 :
- 创建自定义类,实现Runnable接口
- 重写run()方法
- 创建Thread对象,把自定义类作为参数传进去
class Run implements Runnable{@Overridepublic void run() {for (int i = 0; i < 2000; i++) {System.out.println("Run"+i);}}public static void main(String[] args) {new Thread(new Run()).start();} }
实现Callable接口
目的就是为了来处理 Runnable 不支持的用例。 Runnable 接口 不会返回结果或抛出检查异常,但是 Callable 接口 可以。所以,如果任务不需要返回结果或抛出异常推荐使用 Runnable 接口 ,这样代 码看起来会更加简洁。实现步骤 :
- 创建自定义类,实现Callable接口
- 重写call()方法
- 创建自定义类对象
- 创建执行服务(线程池)
- 提交服务
- 获取结果
- 关闭服务
class Cal implements Callable<Boolean> {@Overridepublic Boolean call() throws Exception {return null;}public static void main(String[] args) throws Exception {Cal cal = new Cal();Cal cal1 = new Cal();//创建执行服务ExecutorService executorService = Executors.newFixedThreadPool(2);//提交服务Future<Boolean> future = executorService.submit(cal);Future<Boolean> future1 = executorService.submit(cal1);//获取结果Boolean b = future.get();System.out.println(b);Boolean b1 = future1.get();System.out.println(b1);//关闭服务executorService.shutdown();} }
线程的状态
线程创建之后它将处于 NEW (初始) 状态,调用 start() 方法后开始运行,线程这时候处于 READY (可运行) 状态。可运行状态的线程获得了 CPU 时间片( timeslice )后就处于 RUNNING (运 行) 状态。当线程执行 wait() 方法之后,线程进入 WAITING (等待) 状态。进入等待状态的线程需要依靠其他 线程的通知才能够返回到运行状态,而 TIME_WAITING( 超时等待 ) 状态相当于在等待状态的基础上增加 了超时限制,比如通过 sleep ( long millis ) 方法或 wait ( long millis ) 方法可以将 Java 线程置 于 TIMED WAITING (超时等待) 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE (运行) 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED (阻塞) 状态。线程在执行 Runnable 的 run() 方法之后将会进入到 TERMINATED (终止) 状态。线程执行 wait() 方法之后线程进入 Waiting 状态,由于调用 wait() 时会释放占用的 cpu 资源和锁,所以 当Waiting 线程被其他线程调用 Object.notify() 唤醒之后,需要重新获取对象上的锁。这时候也会先进入 Blocked状态等待获取锁。sleep 和 wait 有什么区别?
- wait 必须搭配 synchronize 一起使用,而 sleep 不需要;
- 进入 wait 状态的线程能够被 notify 和 notifyAll 线程唤醒,而 sleep 状态的线程不能被 notify 方法 唤醒;
- wait 通常有条件地执行,线程会一直处于 wait 状态,直到某个条件变为真,但是 sleep 仅仅让线 程进入睡眠状态;
- wait 方法会释放对象锁,但 sleep 方法不会。
- 在调用 wait 方法之后,线程会变为 WATING(等待)状态,而调用 sleep 方法之后,线程会变为 TIMED_WAITING(超时等待)
ThreadLocal
ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。可以通过set(T) 方法来设置一个值,在 当前线程下 再通过 get()方法获取到原先设置的值。ThreadLocal 是 线程安全 。
- 在Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个 threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本 (即T类型的变量)。
- 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方 法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以 ThreadLocal要保存的副本变量为value,存到threadLocals。
- 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
ThreadLocal造成内存泄漏的原因?
ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用 , 而 value 是强引用。所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来, ThreadLocalMap中就会出现 key 为 null 的 Entry 。假如我们不做任何措施的话 ,value 永远无法被 GC 回 收,这个时候就可能会产生内存泄露。ThreadLocalMap 实现中已经考虑了这种情况,在调用 set() 、 get()、 remove() 方法的时候,会清理掉 key 为 null 的记录。使用完 ThreadLocal 方法后最好手动调用 remove()方法。ThreadLocal内存泄漏解决方案?
每次使用完 ThreadLocal ,都调用它的 remove() 方法,清除数据。在使用线程池的情况下,没有及时清理ThreadLocal ,不仅是内存泄漏的问题,更严重的是可能导致业务逻辑出现问题。所以,使用ThreadLocal就需要用完就清理。
线程同步
volatile
volatile 是 Java 虚拟机提供的 轻量级 的同步机制。volatile 关键字可以保证变量的可见性 ,如果我们将变量声明为 volatile ,这就指示 JVM ,这个变量是共 享且不稳定的,每次使用它都到主存中进行读取。volatile 关键字除了可以保证变量的可见性,还有一个重要的作用就是防止 JVM 的指令重排序 。 如果我们将变量声明为 volatile ,在对这个变量进行读写操作的时候,会通过插入特定的 内存屏障 的方式来禁止指令重排序。volatile 关键字能保证变量的可见性,但不能保证对变量的操作是原子性的。synchronized
老版本 Java 中的 synchronized 属于 重量级锁 ,效率低下。 因为监视器锁( monitor )是依赖于底层的 操作系统的 Mutex Lock 来实现的, Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者 唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核 态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。在 JDK6 之后, Java 官方从 JVM 层面对 synchronized 进行了较大优化。对锁的实现引入了大量的优化,如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。synchronized 关键字解决的是多个线程之间访问资源的同步性, synchronized 关键字可以保证被它修饰 的方法或者代码块在任意时刻只能有一个线程执行。synchronized控制对对象的访问,每个对象一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞;方法一旦执行就独占该线程,直到该方法返回才释放锁,后面被阻 塞的线程才可以获得锁,继续执行。
synchronized的使用
修饰实例方法给 当前对象实例 加锁,进入同步代码前要获得 当前对象实例的锁 。public synchronized void buy() { //同步实例方法 }
修饰静态方法给 当前类 加锁,会作用于类的所有对象实例 ,进入同步代码前要获得 当前 class 的锁 。这是因为静态成 员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,被类的所有实例共享。public static synchronized void method(){ //同步静态方法 }
静态 synchronized 方法 和 非静态 synchronized 方法 之间的调用不互斥。因为访问 静态 synchronized方法 占用的锁是 当前类 的锁,而访问 非静态 synchronized 方法 占用的锁是 当前实例对象 锁。修饰代码块synchronized(object) 表示进入同步代码块前要获得 给定对象的锁 。synchronized(Object.class) 表示进入同步代码前要获得 给定 类的锁 。void method(){synchronized (object/Object.class) {//同步代码块} }
总结
synchronized 关键字加到 static 静态方法和 synchronized(class) 代码块上都是是给 Class 类上锁;synchronized 关键字加到实例方法上是给对象实例上锁;synchronized 和 volatile 的区别
synchronized 关键字和 volatile 关键字是两个互补的存在,而不是对立的存在!
- volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定比 synchronized 关键字要 好 。但是 volatile 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块 。
- volatile 关键字能保证数据的可见性,但不能保证数据的原子性。 synchronized 关键字两者都能保证。
- volatile 关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。
ReentrantLock
ReentrantLock 是一个可重入的互斥锁,又被称为 “ 独占锁 ” 。在同一个时间点只能被一个线程锁持有;可重入表示,ReentrantLock 可以被同一个线程多次获取。ReentraantLock 是通过一个 FIFO 的等待队列来管理获取该锁所有线程的。在 “ 公平锁 ” 的机制下,线程依次排队获取锁;而“ 非公平锁 ” 在锁是可获取状态时,不管自己是不是在队列的开头都会获取锁。class Book implements Runnable {private ReentrantLock lock = new ReentrantLock();static private Integer n = 3;@Overridepublic void run() {lock.lock();if (n <= 0) {lock.unlock();return;}n--;lock.unlock();} }
synchronized 和 ReentrantLock 的区别
两者都是可重入锁。 可重入锁 指的是自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果是不可重入锁的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增 1 ,所以要等到锁的计数器下降为 0 时才能释放锁。
- synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API 。synchronized 是依赖于 JVM 实现的, ReentrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock() 方法配合try/finally 语句块来完成),所以可以通过查看它的源代码,来看它是如何实现的。
- ReentrantLock 比 synchronized 增加了一些高级功能
- 等待可中断 : ReentrantLock 提供了通过 lock.lockInterruptibly() 来实现能够中断等待锁的线程的机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
- 可实现公平锁 : ReentrantLock 可以指定是公平锁还是非公平锁。而 synchronized 只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。 ReentrantLock 默认情况是非公平的,可以通过 ReentrantLock 类的 ReentrantLock(boolean fair) 构造方法来制定是否是 公平的。
- 可实现选择性通知(锁可以绑定多个条件): synchronized 关键字与 wait() 和 notify() / notifyAll() 方法相结合可以实现等待/通知机制。 ReentrantLock 类当然也可以实现,但是需要借助于 Condition 接口与 newCondition() 方法。
线程池
为什么使用线程池
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
创建线程
通过构造方法实现
public class Outer {private static final int CORE_POOL_SIZE = 5;private static final int MAX_POOL_SIZE = 10;private static final Long KEEP_ALIVE_TIME = 1L;private static final int QUEUE_CAPACITY = 100;public static void main(String[] args) {//创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(CORE_POOL_SIZE,MAX_POOL_SIZE,KEEP_ALIVE_TIME,TimeUnit.SECONDS,new ArrayBlockingQueue<>(QUEUE_CAPACITY),new ThreadPoolExecutor.AbortPolicy());Runnable runnable = new Book();for (int i = 0; i < 10; i++) {executor.execute(runnable);}executor.shutdown();while (!executor.isTerminated()) {}} } class Book implements Runnable {@Overridepublic void run() {} }
通过 Executor 框架的工具类 Executors 来实
创建三种类型的 ThreadPoolExecutor :
- newFixedThreadPool : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
public class Outer {public static void main(String[] args) { //创建线程池ExecutorService executor=Executors.newFixedThreadPool(5);Runnable runnable = new Book();for (int i = 0; i < 10; i++) {executor.execute(runnable);}executor.shutdown();while (executor.isTerminated()){}} }
newSingleThreadExecutor : 方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。public class Outer {public static void main(String[] args) {//创建线程池ExecutorService executor = Executors.newSingleThreadExecutor();Runnable runnable = new Book();for (int i = 0; i < 10; i++) {executor.execute(runnable);}executor.shutdown();while (executor.isTerminated()) {}} }
- newCachedThreadPool: 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
public class Outer {public static void main(String[] args) {//创建线程池ExecutorService executor = Executors.newCachedThreadPool();Runnable runnable = new Book();for (int i = 0; i < 10; i++) {executor.execute(runnable);}executor.shutdown();while (executor.isTerminated()) {}} }
Executor 框架
在 Java 5 之后,通过 Executor 来启动线程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免 this 逃逸问题。 this 逃逸是指在构造函 数返回之前其他线程就持有该对象的引用 . 调用尚未构造完全的对象的方法可能引发令人疑惑的错误 。 Executor 框架不仅包括了线程池的管理,还提供了线程工厂、队列以及拒绝策略等, Executor 框架让并发编程变得更加简单。Executor 框架结构(主要由三大部分组成)
- 任务。执行任务需要实现Runnable接口或Callable接口。
- 任务的执行。任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。 Executor框架ThreadPoolExecutor和ScheduledThreadPoolExecutor这两个关键类实现了ExecutorService接口。实际上我们需要更多关注的是 ThreadPoolExecutor 这个类,这个类在我们实际使用线程池的过程中,使用频率还是非常高的。
- 异步计算的结果。包括接口Future和实现Future接口的FutureTask类。
Executor 框架的使用
- 主线程首先要创建实现 Runnable 或者 Callable 接口的任务对象。
- 把创建完成的实现 Runnable / Callable 接口的 对象直接交给 ExecutorService 执行: ExecutorService.execute(Runnable command) )或者也可以把 Runnable 对象或 Callable 对象提交给 ExecutorService 执行( ExecutorService.submit(Runnable task) 或 ExecutorService.submit(Callable <T> task) )。
- 如果执行 ExecutorService.submit(…) , ExecutorService 将返回一个实现 Future 接口的对象(我们刚刚也提到过了执行 execute() 方法和 submit() 方法的区别, submit() 会返回一个 FutureTask 对象)。由于 FutureTask 实现了 Runnable ,我们也可以创建FutureTask ,然后直接交给 ExecutorService 执行。
- 最后,主线程可以执行 FutureTask.get() 方法来等待任务执行完成。主线程也可以执行 FutureTask.cancel(boolean mayInterruptIfRunning) 来取消此任务的执行。
相关文章:

Java-多线程2
什么是线程? 线程是 cpu调度和执行的单位。 多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。 如何实现线程 继承Thread类 实现步骤: 创建自定义类,继承Thread类 重写run方法 创建自定…...

POWER_CONMETRICS的packet wakeup events触发条件的代码走读
摘要: adb shell dumpsys connmetrics 的packet wakeup events触发条件:首先App是无法控制packet wakeup events的事件日志打印,主要App联网过程中网络状态或配置发生变化时由系统netd自动触发的统计。 通俗理解:目前主要表示触…...

Bug:通过反射修改@Autowired注入Bean的字段,明确存在,报错 NoSuchFieldException
【BUG】通过Autowired注入了一个Bean SeqNo,测试的时候需要修改其中的字段。通过传统的反射,无论如何都拿不到信息,关键是一方面可以通过IDEA跳转,一方面debug也确实能看到这个字段。但是每次调用set方法报错:NoSuchFi…...

Vue项目兼容IE11
配置Vue项目兼容IE11详解 Vue 不支持 IE8 及以下版本,因为 Vue 使用了 IE8 无法模拟的 ECMAScript 5 特性。但对于 IE9,Vue 底层是支持。 由于开发过程中,我们经常会使用一些第三方插件或组件,对于这些组件,有时我们…...

可以帮助你快速禁用windows自带的防火墙程序defender control,有效解决占用内存大的问题,供大家学习研究参考
可以关闭windows自带的windows defender防火墙的工具,defender control官方版界面小巧,功能强大,当大家需要手动关闭或禁用windows defender时,就可以使用这款软件,以此来一键关闭或开启这个烦人的系统防火墙。操作起来也非常的简单便捷。 defender control怎么使用 下载…...

2024年9月电子学会Scratch图形化编程等级考试二级真题试卷
2024.09 Scratch图形化编程等级考试二级真题试卷 题目总数:37 总分数:100 一、选择题 第 1 题 Scratch小猫初始坐标是(50,50),小猫向下移动100步后的坐标是?( ) A.(150,50) B.(-50,50) C.(50,-50) D.(50,1…...

STL-vector+题目
vector-顺序表,可以存放任意类型的数据。 vector在[ ]和迭代器、范围for方面的使用差不多一样。 vector的迭代器有普通的还有const类型的迭代器。 vector使用下标[ ]好用。迭代器是容器通用的访问方式,使用方法基本相似。 #include <iostream> #i…...

微服务--Gateway网关--全局Token过滤器【重要】
全局过滤器 GlobalFilter, 注入到 IOC里面即可 概念: 全局过滤器: 所有的请求 都会在执行链里面执行这个过滤器 如添加日志、鉴权等 创建一个全局过滤器的基本步骤: 步骤1: 创建过滤器类 首先,创建一个实现了Globa…...

负载均衡在线判题系统【项目】
项目介绍 本项目是一个负载均衡的在线判题系统 (Online Judge, OJ) 的简易实现。该系统的核心功能是处理大量编程问题的提交,并通过负载均衡的机制,分配判题任务到多台服务器上,确保高效和可靠的评测。系统通过自动选择负载较低的服务器进行…...

重构复杂简单变量之用子类替换类型码
子类替换类型码 是一种用于将类型码替换为子类。当代码使用类型码(通常是 int、string 或 enum)来表示对象的不同类别,并且这些类别的行为有所不同时,使用子类可以更加清晰地表达这些差异并减少复杂的条件判断。 一、什么时候使用…...

【Nginx系列】Nginx配置超时时间
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

2024年龙信
挂载VC的密码:MjAyNOmmeS/oeadrw 手机取证 1. Android 设备在通过 ADB 连接时,通常会要求用户授权连接,会要求用户确认设备授权,并将该设备的公钥保存在 adb_keys文件中 寻找到data/misc/adb/adb_keys下面有中有两个,…...

PyCharm配置Flask开发环境
文章目录 一、步骤1.安装虚拟环境2.创建虚拟环境文件夹3.安装虚拟环境目录4.进入虚拟环境5.active命令 激活6.安装Flask7.在Pycharm中配置Flask环境 总结 一、步骤 1.安装虚拟环境 代码如下(示例): pip install virtualenv 或者 pip insta…...

【人工智能-初级】第2章 机器学习入门:从线性回归开始
文章目录 一、什么是线性回归?二、线性回归的基本概念2.1 一元线性回归2.2 多元线性回归 三、如何进行线性回归建模?四、用Python实现线性回归4.1 导入必要的库4.2 创建虚拟数据集4.3 数据可视化4.4 拆分训练集和测试集4.5 训练线性回归模型4.6 查看模型…...

SPOOLing技术详解,结合实际场景让你了解什么是假脱机技术。
SPOOLing技术 在手工操作阶段,主机直接从I/O设备获取数据,但是由于设备速度很慢,主机速度很快。人机速度矛盾明显,主机需要浪费很多时间来等待设备。 什么是脱机技术,脱机技术可以解决什么问题? 所谓脱…...

基于SSM汽车零部件加工系统的设计
管理员账户功能包括:系统首页,个人中心,员工管理,经理管理,零件材料管理,产品类型管理,产品信息管理,产品出库管理,产品入库管理 员工账号功能包括:系统首页…...

改进 JavaScript 条件语句,探索可以替代 if...else 的 7 种方式!
当优化 JavaScript 代码时,条件语句是一个经常需要思考和改进的关键部分。if...else 结构虽然是我们常用的条件语句之一,但当代码逻辑变得复杂,if...else 结构可能会导致代码冗长、难以维护和理解。因此,了解并掌握优化 if...else…...

全新子比主题7.9.2开心版 子比主题最新版源码
内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 wordpress zibll子比主题7.9.2开心版 修复评论弹授权 可做付费下载站 含wordpress搭建视频教程zibll子比主题安装视频教程支付配置视频教程,视频都是语音讲解,…...

Kafka之消费者组与消费者
消费者(Consumer)在Kafka的体系结构中是用来负责订阅Kafka中的主题(Topic),并从订阅的主题中拉取消息后进行处理。 与其他消息中间件不同,Kafka引入一个逻辑概念——消费组(Consumer Group&…...

Damn-Vulnerable-Drone:一款针对无人机安全研究与分析的靶机工具
关于Damn-Vulnerable-Drone Damn-Vulnerable-Drone是一款针对无人机安全研究与分析的靶机工具,广大研究人员可以利用该环境工具轻松学习、研究和分析针对无人机安全态势。 Damn Vulnerable Drone 基于流行的 ArduPilot/MAVLink 架构,其中故意留下了各种…...

项目模块三:Socket模块
一、模块设计 1、套接字编程常用头文件展示 #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <fcntl.h> 2、成员函数设计 (1…...

Unity-Shader-语义
1、从程序传给顶点函数: POSITION:顶点坐标(模型空间下) NORMAL:法线向量(模型空间下) TANGENT:切线向量(模型空间下) TEXCOORD0~n:纹理坐标 …...

MFC工控项目实例二十四模拟量校正值输入
承接专栏《MFC工控项目实例二十三模拟量输入设置界面》 对模拟量输入的零点校正值及满量程对应的电压值进行输入。 1、在SenSet.h文件中添加代码 #include "BtnST.h" #include "ShadeButtonST.h"/ // SenSet dialogclass SenSet : public CDialog { // Co…...

analysis-ik分词器
analysis-ik分词器 1、安装离线在线 2、使用配置拓展词典 3、测试ik_smartik_max_word 1、安装 离线 使用离线安装下载地址https://release.infinilabs.com/analysis-ik/stable/找到对应es版本的ik分词器、下载zip后放到/elasticsearch/plugins/ik文件夹下。重启es即可生效 …...

开源的、基于内存的键值存储数据库redis详细介绍
目录 一、Redis的概述 1、概述 2、特点 (1)基本特性 (2)数据结构 (3)运行环境 二、Redis的重要作用 1、缓存 2、会话存储 3、消息队列 4、计数器 5、分布式锁 6、排行榜 7、实时数据处理 三、…...

三种容器 std::vector、std::map、std::unordered_set 的对比分析
目录 1.添加元素 1.1 std::vector 1.2 std::map 1.3 std::unordered_set 2. 查找元素 2.1 std::vector 2.2 std::map 2.3 std::unordered_set 3. 遍历容器 3.1 std::vector 使用范围基for循环(range-based for loop) 使用迭代器: 3.2 std::map 3.3 std::unord…...

Nuxt3 SSR 服务端渲染部署 PM2 全流程(Nest.js 同理)
项目打包 我们以 Nuxt3 项目为例子: 项目打包 执行 npm run build , 生成的 .output 文件夹就是部署产物(目前不支持中文路径)执行 npm run preview 可以本地预览效果 方式一:使用 Node 部署 需要服务器安装 Nod…...

如何轻松使用pip安装Git仓库中的私有Python模块(使用pip和Git仓库发布和安装私有Python模块)
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 Git模块 📒📝 Git仓库要求🔖 项目目录结构🔖 文件说明📝 编写setup.py📝 配置MANIFEST.in📝 推送代码到Git仓库📝 使用pip安装模块🔖 使用用户名和密码🔖 使用Personal Access Token (PAT)🔖 示例📝 更…...

写 R 包教程
R R 包开发 | 保姆级教程-CSDN博客 https://www.prestevez.com/post/r-package-tutorial/ 1、加载开发所需 R 包 library(usethis) library(devtools) library(roxygen2) 2、在当前工作路径创建 R 包 usethis::create_package("myRpkg") # 在当前路径创建 my…...

【java】数组(超详细总结)
目录 一.一维数组的定义 1.创建数组 2.初始化数组 二.数组的使用 1.访问数组 2.遍历数组 3.修改数据内容 三.有关数组方法的使用 1.toString 2. copyOf 四.查找数组中的元素 1.顺序查找 2.二分查找binarySearch 五.数组排序 1.冒泡排序 2.排序方法sort 六.数组逆置…...