当前位置: 首页 > news >正文

【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懒汉式&#xff1a;双重检测锁模式的懒汉式单例静态内部类实现单例通过反射破坏单例&#xff0c;修改后的DCL饿汉式枚举实现单例防止反射破坏 …...

Git Bash 和 Git GUI中文汉化

目录 为什么要中文汉化&#xff1f;Git Bash的汉化Git GUI的汉化 为什么要中文汉化&#xff1f; 看到中文大概能猜出是什么意思&#xff0c;便于使用&#xff0c;特别是Git GUI&#xff0c;中文版的菜单很容易理解是要做什么&#xff0c;如下图&#xff1a; Git Bash的汉化 …...

【Ubuntu】Ubuntu常用软件部署

1.安装jdk1.8 (1).apt方式安装 1).安装 1.在终端中输入以下命令&#xff0c;以更新软件包列表 sudo apt-get update2.在终端中输入以下命令&#xff0c;以安装JDK 1.8 sudo apt-get install openjdk-8-jdk3.将Java 1.8设置为默认版本。在终端中输入以下命令 sudo update-…...

Hadoop HA模式切换

Hadoop HA模式下 主从的切换&#xff08;操作命令&#xff09; YARN HA 获取所有RM节点的状态 yarn rmadmin -getAllServiceState获取 rm1 节点的状态 yarn rmadmin -getServiceState rm1手动将 rm1 的状态切换到STANDBY yarn rmadmin -transitionToStandby rm1 ##或者 y…...

自然语言处理(四):全局向量的词嵌入(GloVe)

全局向量的词嵌入&#xff08;GloVe&#xff09; 全局向量的词嵌入&#xff08;Global Vectors for Word Representation&#xff09;&#xff0c;通常简称为GloVe&#xff0c;是一种用于将词语映射到连续向量空间的词嵌入方法。它旨在捕捉词语之间的语义关系和语法关系&#…...

Flink中RPC实现原理简介

前提知识 Akka是一套可扩展、弹性和快速的系统&#xff0c;为此Flink基于Akka实现了一套内部的RPC通信框架&#xff1b;为此先对Akka进行了解 Akka Akka是使用Scala语言编写的库&#xff0c;基于Actor模型提供一个用于构建可扩展、弹性、快速响应的系统&#xff1b;并被应用…...

ELK安装、部署、调试(五)filebeat的安装与配置

1.介绍 logstash 也可以收集日志&#xff0c;但是数据量大时太消耗系统新能。而filebeat是轻量级的&#xff0c;占用系统资源极少。 Filebeat 由两个主要组件组成&#xff1a;harvester 和 prospector。 采集器 harvester 的主要职责是读取单个文件的内容。读取每个文件&…...

Python数据分析案例30——中国高票房电影分析(爬虫获取数据及分析可视化全流程)

案例背景 最近总看到《消失的她》票房多少多少&#xff0c;《孤注一掷》票房又破了多少多少..... 于是我就想自己爬虫一下获取中国高票房的电影数据&#xff0c;然后分析一下。 数据来源于淘票票&#xff1a;影片总票房排行榜 (maoyan.com) 爬它就行。 代码实现 首先爬虫获…...

科技资讯|苹果Vision Pro头显申请游戏手柄专利和商标

苹果集虚拟现实和增强现实于一体的头戴式设备 Vision Pro 推出一个月后&#xff0c;美国专利局公布了两项苹果公司申请的游戏手柄专利&#xff0c;其中一项的专利图如下图所示。据 PatentlyApple 报道&#xff0c;虽然专利本身并不能保证苹果公司会推出游戏手柄&#xff0c;但是…...

Compose学习 - remember、mutableStateOf的使用

一、需求 在显示界面中&#xff0c;数据变动&#xff0c;界面刷新是非常常见的操作&#xff0c;所以使用compose该如何实现呢&#xff1f; 二、remember、mutableStateOf的使用 我们可以借助标题的两个概念 remember、mutableStateOf来完成。这里先不写定义&#xff0c;定义…...

字符串哈希

字符串前缀哈希法 str "ABCABCDEHGJK" 预处理每一个前缀的哈希值,如 : h[0] 0; h[1] "A"的哈希值 h[2] "AB"的哈希值 h[3] "ABC"的哈希值 h[4] "ABCA"的哈希值 问题 : 如何定义一个前缀的哈希值 : 将字符串看…...

【python】【centos】使用python杀死进程后自身也会退出

问题 使用python杀死进程后自身程序也会退出&#xff0c;无法执行后边的代码 这样不行&#xff1a; # 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? 强大的全文搜索和检索功能&#xff1a;Elasticsearch 是一个开源的分布式搜索和分析引擎&#xff0c;使用倒排索引和分布式计算等技术&#xff0c;提供了强大的全文搜索和检索功能。学习 ES 可以掌…...

opencv案例06-基于opencv图像匹配的消防通道障碍物检测与深度yolo检测的对比

基于图像匹配的消防通道障碍物检测 技术背景 消防通道是指在各种险情发生时&#xff0c;用于消防人员实施营救和被困人员疏散的通道。消防法规定任何单位和个人不得占用、堵塞、封闭消防通道。事实上&#xff0c;由于消防通道通常缺乏管理&#xff0c;导致各种垃圾&#xff0…...

练习2:88. 合并两个有序数组

这里写自定义目录标题 题目解体思路代码 题目 给你两个按非递减顺序排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m和 n &#xff0c;分别表示 nums1 和 nums2中的元素数目。 请你合并nums2 到 nums1 中&#xff0c;使合并后的数组同样按非递减顺序排列。 注意&a…...

【代码随想录day23】不同路径

题目 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。 问总共有多少条不同的路径&#xff1f; 示…...

SpringBoot 博客网站

SpringBoot 博客网站 系统功能 登录注册 博客列表展示 搜索 分类 个人中心 文章分类管理 我的文章管理 发布文章 开发环境和技术 开发语言&#xff1a;Java 使用框架: SpringBoot jpa H2 Spring Boot是一个用于构建Java应用程序的开源框架&#xff0c;它是Spring框架的一…...

【分布式搜索引擎elasticsearch】

文章目录 1.elasticsearch基础索引和映射索引库操作索引库操作总结 文档操作文档操作总结 RestAPIRestClient操作文档 1.elasticsearch基础 什么是elasticsearch&#xff1f; 一个开源的分布式搜索引擎&#xff0c;可以用来实现搜索、日志统计、分析、系统监控等功能 什么是…...

wireshark 流量抓包例题

一、题目一(1.pcap) 题目要求&#xff1a; 1.黑客攻击的第一个受害主机的网卡IP地址 2.黑客对URL的哪一个参数实施了SQL注入 3.第一个受害主机网站数据库的表前缀&#xff08;加上下划线例如abc&#xff09; 4.第一个受害主机网站数据库的名字 看到题目SQL注入&#xff0c…...

【Axure视频教程】表格编号函数

今天教大家在Axure里如何使用表格编号函数&#xff0c;包括表格编号函数的基本原理、在需要翻页的中继器表格里如何正确使用该函数、函数作为条件的应用&#xff0c;包括让指定第几行的元件默认变色效果以及更新对应第几行内容的效果。该教程主要讲解表格编号函数&#xff0c;不…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

Linux nano命令的基本使用

参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时&#xff0c;显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)

前言&#xff1a; 双亲委派机制对于面试这块来说非常重要&#xff0c;在实际开发中也是经常遇见需要打破双亲委派的需求&#xff0c;今天我们一起来探索一下什么是双亲委派机制&#xff0c;在此之前我们先介绍一下类的加载器。 目录 ​编辑 前言&#xff1a; 类加载器 1. …...