【JUC基础】JUC入门基础(二)
目录
- 异步回调
- JMM 理解
- 对 volatile 的理解
- 1、保证可见性
- 2、不保证原子性
- 3、禁止指令重排
- 对 JMM 的理解
- 详解单例模式
- 饿汉式
- 懒汉式
- DCL懒汉式:双重检测锁模式的懒汉式单例
- 静态内部类实现单例
- 通过反射破坏单例,修改后的DCL饿汉式
- 枚举实现单例防止反射破坏
- 理解 CAS(compareAndSwap)
- CAS 出现的 ABA 问题
- 理解 ABA 问题
- 解决 ABA 问题(带版本号的原子操作、乐观锁思想)
- 公平锁,非公平锁
- 可重入锁(递归锁)
- 自旋锁
- 排除死锁
异步回调
Future 设计的初衷: 对将来的某个事件的结果进行建模


- 没有返回值的 runAsync 异步回调
import java.util.concurrent.*;/*** 异步调用: CompletableFuture* 异步执行* 成功回调* 失败回调*/
public class test {public static void main(String[] args) throws ExecutionException, InterruptedException {//发起一个请求 void// 没有返回值的 runAsync 异步回调CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "runAsync=>void");});System.out.println("11111111");completableFuture.get();//获取执行结果}
}
- 有返回值的异步回调 supplyAsync
import java.util.concurrent.*;/*** 异步调用: CompletableFuture* 异步执行* 成功回调* 失败回调*/
public class test {public static void main(String[] args) throws ExecutionException, InterruptedException {// completableFuture.get(); // 获取阻塞执行结果// 有返回值的 supplyAsync 异步回调// ajax,成功和失败的回调// 失败返回的是错误信息;CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer");//int i = 10 / 0;return 1024;});//whenComplete: 参数BiConsumer<T, U>// 有两个参数,一个是T 一个是U,T:是代表的 正常返回的结果;U:是代表的 抛出异常的错误信息;System.out.println(completableFuture.whenComplete((t, u) -> {System.out.println("t=>" + t);// 正常的返回结果System.out.println("u=>" + u);// 错误信息:}).exceptionally((e) -> {System.out.println(e.getMessage());return 233;// 可以获取到错误的返回结果}).get());//如果发生了异常,get可以获取到exceptionally返回的值;}
}
JMM 理解
对 volatile 的理解
1、保证可见性
import java.util.concurrent.*;public class test {// 如果不加volatile 程序会死循环// 加了volatile是可以保证可见性的private volatile static Integer number = 0;//main线程public static void main(String[] args) {//子线程1new Thread(()->{while (number==0){}}).start();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}//子线程2new Thread(()->{while (number==0){}}).start();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}number=1;System.out.println(number);}
}
2、不保证原子性
public class test {private static volatile int number = 0;public static void add() {number++;//++ 不是一个原子性操作,是 2~3 个操作}public static void main(String[] args) {//理论上number == 20000for (int i = 1; i <= 20; i++) {new Thread(() -> {for (int j = 1; j <= 1000; j++) {add();}}).start();}//main gcwhile (Thread.activeCount() > 2) {Thread.yield(); //yield让出计算资源并重新竞争资源}System.out.println(Thread.currentThread().getName() + ",num=" + number);//每次都不一样}
}
javap -c test.class反编译字节码文件:


import java.util.concurrent.atomic.AtomicInteger;public class test {// 这些类的底层都直接和操作系统挂钩,是在内存中修改值。private static volatile AtomicInteger number = new AtomicInteger();public static void add(){//number++;number.incrementAndGet(); //底层是 CAS 保证的原子性}public static void main(String[] args) {//理论上number=20000for (int i = 1; i <= 20; i++) {new Thread(()->{for (int j = 1; j <= 1000 ; j++) {add();}}).start();}//main gcwhile (Thread.activeCount()>2){Thread.yield();}System.out.println(Thread.currentThread().getName()+",num="+number);}
}
3、禁止指令重排
源代码 –> 编译器优化重排 –> 指令并行也可能会重排 –> 内存系统也会重排 –> 执行
int x=1; //1
int y=2; //2
x=x+5; //3
y=x*x; //4//期望的执行顺序是 1_2_3_4 可能执行的顺序会变成2134 1324
volatile 可以避免指令重排:volatile 中会加一道内存的屏障,这个内存屏障可以保证在这个屏障中的指令顺序。

对 JMM 的理解
JMM:Java内存模型,不存在的东西,是一个概念,也是一个约定。
关于 JMM 的一些同步的约定:
- 1、线程解锁前,必须把共享变量立刻刷回主存
- 2、线程加锁前,必须读取主存中的最新值到工作内存中
- 3、加锁和解锁是同一把锁
线程中分为工作内存、主内存。
8种操作:(读read,加载load)(使用use,赋值assign)(写write,存储store)(加锁lock,解锁unlock)

内存交互操作有8种,虚拟机实现必须保证每一个操作都是原子的,不可在分的(对于 double 和 long 类型的变量来说,load、store、read 和 write 操作在某些平台上允许例外)
- read(读取):作用于主内存变量,它把一个变量的值从主内存传输到线程的工作内存中,以便随后的load动作使用;
- load(载入):作用于工作内存的变量,它把read操作从主存中变量放入工作内存中;
- use(使用):作用于工作内存中的变量,它把工作内存中的变量传输给执行引擎,每当虚拟机遇到一个需要使用到变量的值,就会使用到这个指令;
- assign(赋值):作用于工作内存中的变量,它把一个从执行引擎中接受到的值放入工作内存的变量副本中;
- store(存储):作用于主内存中的变量,它把一个从工作内存中一个变量的值传送到主内存中,以便后续的write使用;
- write(写入):作用于主内存中的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中;
- lock(锁定):作用于主内存的变量,把一个变量标识为线程独占状态;
- unlock(解锁):作用于主内存的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定;
JMM对这八种指令的使用,制定了如下规则:
- 不允许 read 和 load、store 和 write 操作之一单独出现,必须成对使用。即使用了 read 必须 load,使用了store 必须 write
- 不允许线程丢弃他最近的 assign 操作,即工作变量的数据改变了之后,必须告知主存
- 不允许一个线程将没有 assign 的数据从工作内存同步回主内存
一个新的变量必须在主内存中诞生,不允许工作内存直接使用一个未被初始化的变量。就是怼变量实施 use、store操作之前,必须经过 assign 和 load 操作 - 一个变量同一时间只有一个线程能对其进行 lock。多次 lock 后,必须执行相同次数的 unlock 才能解锁
- 如果对一个变量进行 lock 操作,会清空所有工作内存中此变量的值,在执行引擎使用这个变量前,必须重新 load 或 assign 操作初始化变量的值
- 如果一个变量没有被 lock,就不能对其进行 unlock 操作。也不能unlock一个被其他线程锁住的变量对一个变量进行 unlock 操作之前,必须把此变量同步回主内存

详解单例模式
饿汉式
//饿汉式单例
public class Hungry {//一上来就实例化,可能会浪费空间private byte[] data1 =new byte[1024*1024];private byte[] data2 =new byte[1024*1024];private byte[] data3 =new byte[1024*1024];private byte[] data4 =new byte[1024*1024];//私有化构造器private Hungry() {}private final static Hungry HUNGRY = new Hungry();public Hungry getInstance() {return HUNGRY;}
}
懒汉式
// 懒汉式单例
public class LazyMan {//构造器私有化private LazyMan() {System.out.println(Thread.currentThread().getName()+"OK");}private static LazyMan lazyMan;public static LazyMan getInstance() {if (lazyMan == null) {lazyMan = new LazyMan();}return lazyMan;}//多线程并,会有隐患!public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(()->{lazyMan.getInstance();}).start();}}
}
DCL懒汉式:双重检测锁模式的懒汉式单例
// 懒汉式 DCL单例
public class LazyMan {// 构造器私有化private LazyMan() {System.out.println(Thread.currentThread().getName() + "OK");}// 加volatile,防止指令重排private volatile static LazyMan lazyMan;// 双重检测锁模式的 懒汉式单例 DCL懒汉式public static LazyMan getInstance() {// 第一次检查if (lazyMan == null) {synchronized (LazyMan.class) {// 第二次检查if (lazyMan == null) {lazyMan = new LazyMan();/*** 1. 分配内存空间* 2、执行构造方法,初始化对象* 3、把这个对象指向这个空间* 执行顺序123,132都有可能* A:123 B:132* B把这个对象指向这个空间,发现不为空执行return* 但是此时在线程A中,lazyMan还没有完成构造,lazyMan要加volatile,防止指令重排*/}}}return lazyMan;}//多线程并发public static void main(String[] args) {for (int i = 0; i < 10; i++) {new Thread(() -> {lazyMan.getInstance();}).start();}}
}
静态内部类实现单例
public class Singleton {private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}private Singleton() {}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
通过反射破坏单例,修改后的DCL饿汉式
// 懒汉式单例
public class LazyMan {private volatile static LazyMan lazyMan;//私有化构造器private LazyMan() {synchronized (LazyMan.class) {if (lazyMan != null) {throw new RuntimeException("不要试图使用反射破坏异常");}}System.out.println(Thread.currentThread().getName() + "OK");}// 双重检测锁模式的 懒汉式单例 DCL懒汉式public static LazyMan getInstance() {if (lazyMan == null) {synchronized (LazyMan.class) {if (lazyMan == null) {lazyMan = new LazyMan();// 不是一个原子性操作}}}return lazyMan;}//多线程并发public static void main(String[] args) throws Exception {LazyMan instance = LazyMan.getInstance();Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);declaredConstructor.setAccessible(true);//无视私有LazyMan instance2 = declaredConstructor.newInstance();System.out.println(instance);System.out.println(instance2);}
}
//可以继续破坏
枚举实现单例防止反射破坏
import java.lang.reflect.Constructor;// enum 本身也是一个 Class 类
public enum EnumSingle {INSTANCE;public EnumSingle getInstance(){return INSTANCE;}
}class Test{public static void main(String[] args) throws Exception {EnumSingle instance1 = EnumSingle.INSTANCE;//java.lang.NoSuchMethodException: com.zzy.single.EnumSingle.<init>() 没有空参构造方法//Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);//反编译中,只有有参构造方法 EnumSingle(String s, int i) //java.lang.IllegalArgumentException: Cannot reflectively create enum objectsConstructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);declaredConstructor.setAccessible(true);EnumSingle instance2 = declaredConstructor.newInstance();System.out.println(instance1);System.out.println(instance2);}
}
理解 CAS(compareAndSwap)
CAS : compareAndSet 比较并交换
import java.util.concurrent.atomic.AtomicInteger;public class test {//CAS : compareAndSet 比较并交换public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(2020);//boolean compareAndSet(int expect, int update)//期望值、更新值//如果实际值 和 期望值相同,那么就更新//如果实际值 和 期望值不同,那么就不更新System.out.println(atomicInteger.compareAndSet(2020, 2021));System.out.println(atomicInteger.get());//因为期望值是2020 实际值却变成了2021 所以会修改失败//CAS 是CPU的并发原语atomicInteger.getAndIncrement(); //++操作System.out.println(atomicInteger.compareAndSet(2020, 2021));System.out.println(atomicInteger.get());}
}
CAS : compareAndSet 比较并交换源码:
public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// Java无法操作内存,C++可以,Java通过native方法调用C++// Java通过Unsafe类操作内存 private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {// 获取内存地址的偏移值valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;...public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}
// 内存操作,效率很高
// 自旋锁
// Unsafe类 unsafe.getAndAddInt(this, valueOffset, 1)
// var1 = this; var2 = valueOffset; var4 = 1public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {// 获取内存地址valueOffset中的值 var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));// while中:如果当前对象var1中的内存地址偏移值var2,这个值如果还等于var5,那么这个值等于var5+var4return var5;}
CAS 出现的 ABA 问题
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作,如果不是就一直循环,使用的是自旋锁。
缺点:
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- 存在ABA问题
理解 ABA 问题
狸猫换太子:
- 线程1:期望值是1,要变成2;
- 线程2:两个操作:
1、期望值是1,变成3
2、期望是3,变成1
所以对于线程1来说,A的值还是1,所以就出现了问题,骗过了线程1
import java.util.concurrent.atomic.AtomicInteger;public class test {// CAS compareAndSet : 比较并交换public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(2020);//public final boolean compareAndSet(int expect, int update)// 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语!// ============== 捣乱的线程 ==================System.out.println(atomicInteger.compareAndSet(2020, 2021));System.out.println(atomicInteger.get());System.out.println(atomicInteger.compareAndSet(2021, 2020));System.out.println(atomicInteger.get());// ============== 期望的线程 ==================System.out.println(atomicInteger.compareAndSet(2020, 77777));System.out.println(atomicInteger.get());}
}
解决 ABA 问题(带版本号的原子操作、乐观锁思想)
解决 ABA 问题:引入原子引用,对应的思想:乐观锁。
思想:带版本号的原子操作

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;public class test {//AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题,正常在业务操作,这里面比较的都是一个个对象static AtomicStampedReference<Integer> atomicStampedReference = newAtomicStampedReference<>(1, 1);// CAS compareAndSet : 比较并交换public static void main(String[] args) {new Thread(() -> {// 获得版本号int stamp = atomicStampedReference.getStamp();System.out.println("a1版本号=>" + stamp);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 修改操作时,版本号更新 + 1atomicStampedReference.compareAndSet(1, 2,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1);System.out.println("a2版本号=>" + atomicStampedReference.getStamp());// 重新把值改回去, 版本号更新 + 1System.out.println(atomicStampedReference.compareAndSet(2, 1,atomicStampedReference.getStamp(),atomicStampedReference.getStamp() + 1));System.out.println("a3版本号=>" + atomicStampedReference.getStamp());}, "a").start();// 乐观锁的原理相同!new Thread(() -> {// 获得版本号int stamp = atomicStampedReference.getStamp();System.out.println("b1=>" + stamp);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomicStampedReference.compareAndSet(1, 3,stamp, stamp + 1));System.out.println("b2=>" + atomicStampedReference.getStamp());}, "b").start();}
}
公平锁,非公平锁
- 公平锁: 非常公平, 不能够插队,必须先来后到
- 非公平锁:非常不公平,可以插队 (默认都是非公平)
// ReentrantLock 源码
public class ReentrantLock implements Lock, java.io.Serializable {...public ReentrantLock() {sync = new NonfairSync();}public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();}...
}
可重入锁(递归锁)
拿到外面的锁之后,就可以自动获得拿到里面的锁
//Synchronized
public class test {public static void main(String[] args) {Phone phone = new Phone();new Thread(() -> {phone.sms();}, "A").start();new Thread(() -> {phone.sms();}, "B").start();}
}class Phone {//外面一把锁public synchronized void sms() {System.out.println(Thread.currentThread().getName() + ":sms");//里面一把锁call();}public synchronized void call() {System.out.println(Thread.currentThread().getName() + ":call");}
}
//一定是
/*
A:sms
A:call
B:sms
B:call
*/
Lock 锁必须配对,相当于 lock 和 unlock 必须数量相同,在外面加的锁,也可以在里面解锁;在里面加的锁,在外面也可以解锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;//Lock
public class test {public static void main(String[] args) {Phone2 phone = new Phone2();new Thread(() -> {phone.sms();}, "A").start();new Thread(() -> {phone.sms();}, "B").start();}
}class Phone2 {Lock lock = new ReentrantLock();public void sms() {lock.lock();try {System.out.println(Thread.currentThread().getName() + ":sms");//里面还有锁call();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void call() {lock.lock();try {System.out.println(Thread.currentThread().getName() + ":call");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}
自旋锁
import java.util.concurrent.atomic.AtomicReference;//自旋锁
public class SpinlockDemo {//初始: int -> 0; 引用类型 Thread -> nullAtomicReference<Thread> atomicReference = new AtomicReference<>();// 加锁public void myLock() {Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName() + "==> myLock");//自旋锁while (!atomicReference.compareAndSet(null, thread)) {}}// 解锁public void myUnlock() {Thread thread = Thread.currentThread();System.out.println(Thread.currentThread().getName() + "==> myUnlock");//自旋锁atomicReference.compareAndSet(thread, null);}
}
import java.util.concurrent.TimeUnit;public class TestSpinLock {public static void main(String[] args) {//ReentrantLock
/* ReentrantLock reentrantLock = new ReentrantLock();reentrantLock.lock();reentrantLock.unlock();*/// 底层使用的自旋锁 CASSpinlockDemo lock = new SpinlockDemo();new Thread(()->{lock.myLock();try {TimeUnit.SECONDS.sleep(3);} catch (Exception e) {e.printStackTrace();} finally {lock.myUnlock();}},"T1").start();new Thread(()->{lock.myLock();try {TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();} finally {lock.myUnlock();}},"T2").start();}
}
/*
T1==> myLock
T2==> myLock //必须等T1解锁,自旋
T1==> myUnlock
T2==> myUnlock
*/
排除死锁
A、B各持有锁,试图获取对方的锁,例如:
import java.util.concurrent.TimeUnit;public class test {public static void main(String[] args) {String lockA = "lockA";String lockB = "lockB";new Thread(new MyThread(lockA, lockB), "T1").start();new Thread(new MyThread(lockB, lockA), "T2").start();}
}class MyThread implements Runnable {private String lockA;private String lockB;public MyThread(String lockA, String lockB) {this.lockA = lockA;this.lockB = lockB;}@Overridepublic void run() {synchronized (lockA) {System.out.println(Thread.currentThread().getName() + "lock: " + lockA + "=> get: " + lockB);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}synchronized (lockB) {System.out.println(Thread.currentThread().getName() + "lock: " + lockB + "=>get: " + lockA);}}}
}
使用 jstack 进程号,找到死锁问题,进程号由 jps -l 得到
...
"T2":at JUC_learn.MyThread.run(test.java:36)- waiting to lock <0x053bedc8> (a java.lang.String)- locked <0x053bedf0> (a java.lang.String)at java.lang.Thread.run(Thread.java:745)
"T1":at JUC_learn.MyThread.run(test.java:36)- waiting to lock <0x053bedf0> (a java.lang.String)- locked <0x053bedc8> (a java.lang.String)at java.lang.Thread.run(Thread.java:745)Found 1 deadlock.
相关文章:
【JUC基础】JUC入门基础(二)
目录 异步回调JMM 理解对 volatile 的理解1、保证可见性2、不保证原子性3、禁止指令重排 对 JMM 的理解 详解单例模式饿汉式懒汉式DCL懒汉式:双重检测锁模式的懒汉式单例静态内部类实现单例通过反射破坏单例,修改后的DCL饿汉式枚举实现单例防止反射破坏 …...
Git Bash 和 Git GUI中文汉化
目录 为什么要中文汉化?Git Bash的汉化Git GUI的汉化 为什么要中文汉化? 看到中文大概能猜出是什么意思,便于使用,特别是Git GUI,中文版的菜单很容易理解是要做什么,如下图: Git Bash的汉化 …...
【Ubuntu】Ubuntu常用软件部署
1.安装jdk1.8 (1).apt方式安装 1).安装 1.在终端中输入以下命令,以更新软件包列表 sudo apt-get update2.在终端中输入以下命令,以安装JDK 1.8 sudo apt-get install openjdk-8-jdk3.将Java 1.8设置为默认版本。在终端中输入以下命令 sudo update-…...
Hadoop HA模式切换
Hadoop HA模式下 主从的切换(操作命令) YARN HA 获取所有RM节点的状态 yarn rmadmin -getAllServiceState获取 rm1 节点的状态 yarn rmadmin -getServiceState rm1手动将 rm1 的状态切换到STANDBY yarn rmadmin -transitionToStandby rm1 ##或者 y…...
自然语言处理(四):全局向量的词嵌入(GloVe)
全局向量的词嵌入(GloVe) 全局向量的词嵌入(Global Vectors for Word Representation),通常简称为GloVe,是一种用于将词语映射到连续向量空间的词嵌入方法。它旨在捕捉词语之间的语义关系和语法关系&#…...
Flink中RPC实现原理简介
前提知识 Akka是一套可扩展、弹性和快速的系统,为此Flink基于Akka实现了一套内部的RPC通信框架;为此先对Akka进行了解 Akka Akka是使用Scala语言编写的库,基于Actor模型提供一个用于构建可扩展、弹性、快速响应的系统;并被应用…...
ELK安装、部署、调试(五)filebeat的安装与配置
1.介绍 logstash 也可以收集日志,但是数据量大时太消耗系统新能。而filebeat是轻量级的,占用系统资源极少。 Filebeat 由两个主要组件组成:harvester 和 prospector。 采集器 harvester 的主要职责是读取单个文件的内容。读取每个文件&…...
Python数据分析案例30——中国高票房电影分析(爬虫获取数据及分析可视化全流程)
案例背景 最近总看到《消失的她》票房多少多少,《孤注一掷》票房又破了多少多少..... 于是我就想自己爬虫一下获取中国高票房的电影数据,然后分析一下。 数据来源于淘票票:影片总票房排行榜 (maoyan.com) 爬它就行。 代码实现 首先爬虫获…...
科技资讯|苹果Vision Pro头显申请游戏手柄专利和商标
苹果集虚拟现实和增强现实于一体的头戴式设备 Vision Pro 推出一个月后,美国专利局公布了两项苹果公司申请的游戏手柄专利,其中一项的专利图如下图所示。据 PatentlyApple 报道,虽然专利本身并不能保证苹果公司会推出游戏手柄,但是…...
Compose学习 - remember、mutableStateOf的使用
一、需求 在显示界面中,数据变动,界面刷新是非常常见的操作,所以使用compose该如何实现呢? 二、remember、mutableStateOf的使用 我们可以借助标题的两个概念 remember、mutableStateOf来完成。这里先不写定义,定义…...
字符串哈希
字符串前缀哈希法 str "ABCABCDEHGJK" 预处理每一个前缀的哈希值,如 : h[0] 0; h[1] "A"的哈希值 h[2] "AB"的哈希值 h[3] "ABC"的哈希值 h[4] "ABCA"的哈希值 问题 : 如何定义一个前缀的哈希值 : 将字符串看…...
【python】【centos】使用python杀死进程后自身也会退出
问题 使用python杀死进程后自身程序也会退出,无法执行后边的代码 这样不行: # cmd " ps -ef | grep -v grep | grep -E task_pull_and_submit.py$|upgrade_system.py$| awk {print $2}"# pids os.popen(cmd).read().strip(\n).split(\n)# p…...
【ES系列】(一)简介与安装
首发博客地址 首发博客地址[1] 系列文章地址[2] 教学视频[3] 为什么要学习 ES? 强大的全文搜索和检索功能:Elasticsearch 是一个开源的分布式搜索和分析引擎,使用倒排索引和分布式计算等技术,提供了强大的全文搜索和检索功能。学习 ES 可以掌…...
opencv案例06-基于opencv图像匹配的消防通道障碍物检测与深度yolo检测的对比
基于图像匹配的消防通道障碍物检测 技术背景 消防通道是指在各种险情发生时,用于消防人员实施营救和被困人员疏散的通道。消防法规定任何单位和个人不得占用、堵塞、封闭消防通道。事实上,由于消防通道通常缺乏管理,导致各种垃圾࿰…...
练习2:88. 合并两个有序数组
这里写自定义目录标题 题目解体思路代码 题目 给你两个按非递减顺序排列的整数数组 nums1 和 nums2,另有两个整数 m和 n ,分别表示 nums1 和 nums2中的元素数目。 请你合并nums2 到 nums1 中,使合并后的数组同样按非递减顺序排列。 注意&a…...
【代码随想录day23】不同路径
题目 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径? 示…...
SpringBoot 博客网站
SpringBoot 博客网站 系统功能 登录注册 博客列表展示 搜索 分类 个人中心 文章分类管理 我的文章管理 发布文章 开发环境和技术 开发语言:Java 使用框架: SpringBoot jpa H2 Spring Boot是一个用于构建Java应用程序的开源框架,它是Spring框架的一…...
【分布式搜索引擎elasticsearch】
文章目录 1.elasticsearch基础索引和映射索引库操作索引库操作总结 文档操作文档操作总结 RestAPIRestClient操作文档 1.elasticsearch基础 什么是elasticsearch? 一个开源的分布式搜索引擎,可以用来实现搜索、日志统计、分析、系统监控等功能 什么是…...
wireshark 流量抓包例题
一、题目一(1.pcap) 题目要求: 1.黑客攻击的第一个受害主机的网卡IP地址 2.黑客对URL的哪一个参数实施了SQL注入 3.第一个受害主机网站数据库的表前缀(加上下划线例如abc) 4.第一个受害主机网站数据库的名字 看到题目SQL注入,…...
【Axure视频教程】表格编号函数
今天教大家在Axure里如何使用表格编号函数,包括表格编号函数的基本原理、在需要翻页的中继器表格里如何正确使用该函数、函数作为条件的应用,包括让指定第几行的元件默认变色效果以及更新对应第几行内容的效果。该教程主要讲解表格编号函数,不…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
离线语音识别方案分析
随着人工智能技术的不断发展,语音识别技术也得到了广泛的应用,从智能家居到车载系统,语音识别正在改变我们与设备的交互方式。尤其是离线语音识别,由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力,广…...
数据库——redis
一、Redis 介绍 1. 概述 Redis(Remote Dictionary Server)是一个开源的、高性能的内存键值数据库系统,具有以下核心特点: 内存存储架构:数据主要存储在内存中,提供微秒级的读写响应 多数据结构支持&…...
Canal环境搭建并实现和ES数据同步
作者:田超凡 日期:2025年6月7日 Canal安装,启动端口11111、8082: 安装canal-deployer服务端: https://github.com/alibaba/canal/releases/1.1.7/canal.deployer-1.1.7.tar.gz cd /opt/homebrew/etc mkdir canal…...
