并发线程、锁、ThreadLocal

news2024/11/25 16:56:10

并发编程

  • 并发编程
      • Java内存模型(JMM)
      • 并发编程核心问题—可见性、原子性、有序性
      • volatile关键字
      • 原子性
      • 原子类
      • CAS(Compare-And-Swap 比较并交换)
        • ABA问题
      • Java中的锁
        • 乐观锁和悲观锁
        • 可重入锁
        • 读写锁
        • 分段锁
        • 自旋锁
        • 共享锁/独占锁
        • 公平锁/非公平锁
        • 偏向锁/轻量级锁/重量级锁
          • 偏向锁状
          • 轻量级锁
          • 重量级锁
        • 对象结构
        • sychronized锁实现
        • AQS
        • ReentrantLock锁实现
      • JUC常用类
        • ConcurrentHashMap
        • CopyOnWriteArrayList
        • CopyOnWriteArrayList
        • 辅助类CountDownLatch
      • 对象引用
        • 强引用
        • 软引用(SoftReference)内存不足即回收
        • 弱引用(Weak Reference)发现即回收
        • 虚引用(Phantom Reference):对象回收跟踪
      • 线程池
        • **TheadPollExecutor**类
        • 构造器中各个参数的含义
        • 线程池的执行
        • 线程池中的队列
        • 线程池的拒绝策略
        • 关闭线程池
      • ThreadLocal
        • ThreadLocal 内存泄漏问题

并发编程

并行:同一个节点同时发生

并发:在一段时间内,多个事件交替执行

并发编程:在例如买票、抢购等场景下,有大量请求访问同一资源,会出现线程安全的问题,所以需要通过编程来解决让多个线程依次访问资源,称为并发编程

Java内存模型(JMM)

java内存模型,是java虚拟机规范的一种工作模式

JMM将内存分为主内存和工作内存。变量数据存储在主内存中,线程在操作变量时,会将主内存中的数据复制到工作内存,在工作内存中操作完成后,再写回主内存

在这里插入图片描述

并发编程核心问题—可见性、原子性、有序性

基于java内存模型的设计,多线程操作一些共享数据时,会出现三个问题

不可见性:多个线程分别对共享数据进行操作,彼此之间不可见,操作结束写回主内存,可能会出现问题

无序性:为了性能,对一些代码执行的执行顺序进行重排,以提高速度

非原子性:线程切换带来的原子性问题

volatile关键字

共享变量被volatile修饰以后:

1.共享变量被一个线程修改后,对其他线程立即可见

2.在执行过程中不会被重排

3.不能保证对变量操作的原子性

volatile底层实现原理

使用Memory Barrier(内存屏障),内存屏障是一条指令,它可以对编译器和处理器的指令重排做出一i的那个的限制。

有序性实现:volatile修饰的变量在操作前,添加内存屏障,不让他的指令干扰

可见性实现:主要通过Lock前缀指令+MESI缓存一致性协议来实现。操作volatile修饰的变量时,JVM会发送Lock前缀指令给CPU,CPU在执行完操作后,会立即将新值刷新到内存,其他CPU都会对总线嗅探,看自己本地缓存的数据是否被修改,如果修改了,就会将修改的数据存到本地缓存中,主内存就h会加载最新的值。

原子性

通过加锁的方式,让线程互斥执行来保证一次只有一个线程执行

锁:synchronized关键字,是锁的一种实现。synchronized一定能保证原子性,也能够保证可见性和有序性。

原子变量:JUC(java.util.concurrent包)中的locks包和atmic包,可以解决原子性问题

加锁是一种阻塞式方式实现,原子变量是非阻塞方式实现。

原子类

原子类的原子性是通过volatile+CAS实现原子操作,适合与低并发的条件下

CAS(Compare-And-Swap 比较并交换)

CAS是乐观锁的一种实现方式,采用的是自选锁的思想,是一种轻量级的锁机制
底层是通过 Unsafe 类中的 compareAndSwapInt 等方法实现.

CAS包含了三个操作数: 内存值 V、预估值 A、更新值 B

过程:1.第一次将主内存中的值放到工作内存中作为预期值,然后将更新值存入工作内存。

​ 2.将工作内存中的值写入主内存前需要把预期值和主内存的值进行比较。

​ 3.如果主内存的值和预期值相等,将更新值写入主内存,如果不相等,说明有其他线程修改了主内存的值,需要重复上述过程,直到主内存的值和预估值相等。

在这里插入图片描述

缺点:CUP的消耗增加

ABA问题

ABA问题,即某个线程将内存值A改为了B,再又A改为了A,当另一个线程是使用预期值去判断时,内存值和与预期值相等,无法判断内存值是否发生过变化

在这里插入图片描述

解决方式:通过使用类添加版本号,来避免问题的发生,如原先的内存值为(A,1),线程将(A,1)修改为了(B,2),再由(B,2)修改为(A,3)。此时另一个线程使用预期值(A,1)与内存值(A,3)进行比较,只需要比较版本号 1 和3.即可发现内存值被更新过。

Java中的锁

java中锁的名词不是全指的是锁。还可以指的是锁的特性、锁的状态、锁的设计

乐观锁和悲观锁

乐观锁:认为同一个数据并发的操作是不会发生修改的,不加锁的方式实现是没有问题的,每次操作前判断(CAS)是否成立。

悲观锁:认为同一个数据并发的操作会发生修改,必须加锁

可重入锁

当一个线程获取外层方法的同步锁对象后,可以获取到内部其他同步锁

public class Demo{
      synchronized void setA throws Exception{
              System.out.println(A);
              setB();
      }

      synchronized void setB throws Exception{
             System.out.println(B);
   
      }
}

上面的代码就是一个可重入锁的一个特点,如果不是可重入锁,setB不会被当前线程执行,造成死锁

读写锁

支持读写加锁,如果都是读操作,那么就不加锁,如果存在写操作,就会出现操作互斥

读写锁为了防止脏读

private  int data;//共享数据
    private ReadWriteLock rwl=new ReentrantReadWriteLock();
    public  void set(int data){
        rwl.writeLock().lock();//获取到写锁
        try {
            System.out.println(Thread.currentThread().getName()+"准备写入数据");
            this.data=data;
            System.out.println(Thread.currentThread().getName()+"写入数据"+this.data);

        }finally {
            rwl.writeLock().unlock();//释放写锁
        }
        
    }
    public  void get(){
        rwl.readLock().lock();//取到读锁
        try {
            System.out.println(Thread.currentThread().getName()+"准备写入数据");
            System.out.println(Thread.currentThread().getName()+"写入数据"+this.data);

        }finally {
            rwl.readLock().unlock();//释放读锁
        }
    }

分段锁

不是锁,是一种锁的实现思想,用于将数据分段,给每个分段数据加锁提高并发效率

自旋锁

不是锁,以自旋的方式进入锁。自旋锁是比较消耗CPU

共享锁/独占锁

共享锁:该锁可被多个线程共享。读写锁中的读锁是共享锁

独占锁:是指该锁只能被一个线程拥有。Synchronized

公平锁/非公平锁

公平锁:是按请求的顺序来获取锁,先来后到,例如ReentrantLock

非公平锁:没有顺序,谁抢到谁执行。例如synchronized

偏向锁/轻量级锁/重量级锁

synchronized锁的状态存储在同步锁对象的对象头中的区域Mark Word中存储

锁的状态有四种:无锁状态、偏向锁状态、轻量级锁、重量级锁

偏向锁状

代码一直被一个线程访问,那么该线程会自动获取锁

轻量级锁

当锁的状态是偏向锁时,又有一个线程访问,轻量级锁就会升级为轻量级锁,其他线程就会通过自旋的方式获取锁,不会阻塞。

重量级锁

当锁的状态为轻量级锁时,当线程自选到一定的次数就会进入阻塞状态,锁状态升级为重量级锁,等待操作系统调度。

对象结构

在Hotspot虚拟机中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充,synchronized使用的锁对象是存储在java对象头中。

在这里插入图片描述

对象头中有一块Mark word,用于存储对象自身运行时的数据,如哈希码、GC分代年龄、锁状态、线程持有的锁、偏向锁等等。

下面就是对象头的一些信息:
在这里插入图片描述

sychronized锁实现

Java提供的一种原子性内置锁。synchronized基于进入和退出监视器对象来实现方法同步和代码块同步。

同步方法使用 ACC_SYNCHRONIZED标记是否为同步方法,当方法调用时,会检查方法是否被标记,如果被标记,线程进入该方法时,需要monitorenter,退出方法时需要monitorexit.

代码块的同步是利用 monitorenter 和 monitorexit 这两个字节码指令

monitorenter指令:尝试获取对象的锁,如果获取到,把锁的计数器加1

monitorexit:将锁计数器减一,当计数器为0时,锁就被释放

Java 中 synchronized 通过在对象头设置标记,达到了获取锁和释放锁的目的。

AQS

AQS(抽象同步队列),是JUC中的核心组件,其他锁实现的基础

实现原理:

在类中维护一个state变量表示锁是否使用,然后还维护一个队列,以及获取锁,释放锁的方法

当线程创建后,先判断state值,当state=0,没有线程使用,当state=1,线程会去队列等待。

等占有state的线程执行完成将state-1后,会唤醒对列中等待的线程(head中的下一个结点)去获取state;
在这里插入图片描述

AbstractQueuedSynchronizer 成员

private transient volatile Node head;
private transient volatile Node tail;
/*使用变量 state 表示锁状态,0-锁未被使用,大于 0 锁已被使用
共享变量 state,使用 volatile 修饰保证线程可见性
*/
private volatile int state;

状态信息通过 getState , setState , compareAndState来操作

protected final int getState() { //获得锁状态
        return state;
    }
    protected final void setState(int newState) {//设置锁状态
        state = newState;
    }
    //使用 CAS 机制设置状态
    protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

获取锁的方式有两种:

tryAcqurie () :尝试获取锁。

acquire():尝试获取锁,获取失败时,进入队列等待。直到获取

public final void acquire(int arg) {
       //tryAcquire获取锁成功,方法结束,获取锁失败,执行  acquireQueued(addWaiter(Node.EXCLUSIVE), arg)将线程arg添加到队列中。
       if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}

addWaiter: 尝试获取锁失败后,将当前线程封装到一个 Node 对象中,添加
到队尾,并返回 Node 节点. acquireQueued: 将线程添加到队列后,以自旋的方式去获取锁
release 释放锁
tryRelease: 释放锁,将 state 值进行修改为 0
unparkSuccessor: 唤醒节点的后继者(如果存在)

AQS的锁模式分为:独占和共享

ReentrantLock锁实现

ReentrantLock基于AQS,可以实现公平锁和非公平锁

ReentrantLock有Sync、NonfairSync、FairSync三个内部类,他们紧密相关

在这里插入图片描述

NonfairSync继承了Sync类,实现非公平锁

static final class NonfairSync extends Sync {
//加锁
final void lock() {
//若通过 CAS 设置变量 state 成功,就是获取锁成功,则将当前线程设置为独占线程。
//若通过 CAS 设置变量 state 失败,就是获取锁失败,则进入 acquire 方法进行后续处理。
    if (compareAndSetState(0, 1))
         setExclusiveOwnerThread(Thread.currentThread());
      else
        acquire(1);
    }
  //尝试获取锁,无论是否获得都立即返回
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

FairSync 类也继承了 Sync 类,实现公平锁

static final class FairSync extends Sync {
    final void lock() {
        // 以独占模式获取对象,忽略中断
        acquire(1);//底层实现交由 AbstractQueuedSynchronizer
    }
}

JUC常用类

ConcurrentHashMap

ConcurrentHashMap是线程安全的哈希表,在多线程操作中,比Hashtable效率高,内部使用cas+synchronized(分段锁被弃用)。

放弃分段锁的原因:浪费内存,在运行环境中,map中同时进入同一个位置的概率很小,分段所反而会浪费更多的时间。

jdk8放弃分段锁,使用Node锁。提高了性能,并使用CAS操作来保证Node操作的原子性。

过程:

put时,通过hash找到对应链表后,查看是否是第一个Node,如果是,直接用cas原则插入。

如果不是,则直接用链表第一个Node加synchornized锁。

在这里插入图片描述

ConcurrentHashMap和Hashtable一样 不支持存储 null 键和 null 值. 这样是为了消除歧义

不能put null是因为 无法分辨key没找到返回null还是有key值为null,所以不能 null.

不等存储null值,是因为当你get(k)获取value时,如果获取到null时,你无法判断是value值为null,还是这个key还没做过映射

CopyOnWriteArrayList

ArrayList是线程不安全的,Vector是线程安全,vector读操作和写操作都加了锁,实际应用中读操作很频繁,且读操作不会修改数据。所以CopyOnWriteArrayList,为了提高性能出现。

CopyOnWriteArrayList修改数据流程:当list需要被修改时,并不直接对原有list进行修改,而是对原有数进行拷贝,将修改的内容写入副本中,修改结束后,将修改完的副本替换成原来的数据

CopyOnWriteArrayList

CopyOnWriteArrayList实现基于CopyOnWriteArrayList,不能存储重复元素数据

辅助类CountDownLatch

CountDownLatch,底层时通过AQS来完成的,一个线程等待其他线程执行完才执行。

过程:创建CountDownLatch 对象指定一个线程数量。每当一个线程执行完毕后,AQS内部的state-1,当state=0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以执行

CountDownLatch countDownLatch = new CountDownLatch(6);//设置线程总量

        for (int i = 0; i <6 ; i++) {
           new Thread(()->{
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("aaaaaaaaaaaaa");
               countDownLatch.countDown();
           }).start();
        }
        countDownLatch.await();

        System.out.println("main线程执行");//最后执行的内容

对象引用

强引用

对象由引用指向的,如 Object obj=new Object();这种情况下永远不会被垃圾回收器回收

软引用、弱引用、虚引用都是用来标记对象的一种状态

软引用(SoftReference)内存不足即回收

软引用是用来描述一些还有用但非必须的对象,如果内存充足的情况下,可以保留软引用,如果内存不足。经过一次垃圾回收后,内存依然不足,软引用的对象就会被清除

弱引用(Weak Reference)发现即回收

弱引用的对象也是描述非必须的对象,它只能存活到下一次垃圾回收发生为止,当垃圾回收器和工作,就会回收掉弱引用的对象

虚引用(Phantom Reference):对象回收跟踪

虚引用的对象和没有引用几乎是一样的,随时都会被垃圾回收器回收,虚引用必须和引用队列一起使用。虚引用在创建时必须提供一个引用队列作为参数,目的是在这个对象被回收时收到系统通知

线程池

线程池就是事先创建一些线程,每次使用时直接获取,用完不销毁

Executors提供了常见的线程池创建方法:

newSingleThreadExecutor:一个单线程的线程池。如果因异常结束,会再创建一个新的,保证按照提交顺序执行。
newFixedThreadPool:创建固定大小的线程池。根据提交的任务逐个增加线程,直到最大值保持不变。如果因异常结束,会新创建一个线程补充。
newCachedThreadPool:创建一个可缓存的线程池。会根据任务自动新增或回收线程

通常情况下不建议直接只用Executors来创建线程池

线程池的优点:降低资源消耗,提高响应速度,节省创建时间

TheadPollExecutor

Java.uitl.concurrent.ThreadPoolExecutor 类是线程池中最核心的一个类,ThreadPoolExecutor 继承了 AbstractExecutorService 类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。

构造器中各个参数的含义

**corePoolSize:**核心线程池的大小,默认情况,创建线程池,线程池中线程数为0,当有任务来时,就会去创建一个线程执行任务,当线程数目达到corePoolSize,就会把到达的任务放到缓存队列。prestartAllCoreThreads()或者 prestartCoreThread()方法是在没有任务到来之前就创建corePoolSize个线程或一个线程

**maximumPoolSize:**线程池最大线程数。

**keepAliveTime:**表示线程没有任务执行时最多保持多长时间停止。只有当线程池中的线程数量大于corePoolSize时,keepAliveTime才会起作用,直到线程数小于corePoolSize时,keepAliveTime才终止。

unit:参数 keepAliveTime 的时间单位,有 7 种取值

workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响 。

**threadFactory:**线程工厂,主要用来创建线程;
**handler:**表示当拒绝处理任务时的策略

线程池的执行

创建完成ThreadPoolExecutor,当向线程池提交任务时,通常使用 execute方法。

提交执行流程如图:

在这里插入图片描述

1.如果线程池中的存在的核心线程数小于corePoolSize时,线程池会创建一个核心线程去执行任务

2.如果核心线程数已满,任务会被放进任务队列workQueue排队执行

3.如果任务队列已满,且线程数小于maximumPoolSize时,创建一个非核心线程执行提交的任务。

4.如果当前线程数达到maximumPoolSize时。直接采用拒绝策略处理

线程池中的队列

ArrayBlockingQueue:是数组实现的有界的阻塞队列,必须给定最大容量

LinkedBlockingQueue:基于链表结构的阻塞队列,按 FIFO 排序任务,容量可以选择进行设置,不设置是一个最大长度为 Integer.MAX_VALU

线程池的拒绝策略

构造方法中的RejectedExecutionHandler用于指定线程池的拒绝策略。拒绝策略用于请求任务太多,线程池处理不过来的情况。

默认有四种类型:

AbortPolicy:直接抛出异常,拒绝执行

CallerRunsPolicy:将任务交给提交任务的线程(如:main方法)来执行此任务

DiscardOleddestPolicy:该策略会丢弃等待时间最长的任务,也就是最后即将被执行的任务,并尝试再次提交当前任务。

DiscardPolicy:直接丢弃当前提交的任务,不执行

excute与submit的区别:execute 适用于不需要关注返回值的场景,submit 方法适用于需要关注返
回值的场景。

关闭线程池

关闭线程池可以调用 shutdownNow 和 shutdown 两个方法来实现

shutdownNow:直接关闭线程池,对正在执行的任务全部发出 interrupt(),停止执行,对还未开始执行的任务全部取消,并且返回还没开始的任务列表.

shutdown:当我们调用 shutdown 后,等待线程池中的任务执行完,关闭线程池。

ThreadLocal

ThreadLocal线程变量,用来创建一个变量,该变量可以被多线程使用且互不干扰。为线程私有。

//创建一个ThreadLocal对象,复制保用来为每个线程会存一份变量,实现线程封闭
    private  static ThreadLocal<Integer> localNum = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 0;
        }
    };

原理:ThreadLocal是一个泛型类,可以存放任何类型的对象,他的类中定义了一个map,用来存放ThreadLocal对象和变量值。ThreadLocal 实 现 了 一 个 ThreadLocalMap 的静态类ThreadLocalMap类中的get(),set()来改变变量值。

ThreadLocal set方法

 //set 方法
 public void set(T value) {
        //获取当前线程对象
        Thread t = Thread.currentThread();
       //判断该对象是否已经存入map
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
// 获取 threadLocalmap
 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
//创建threadLocalmap,将ThreadLocal对象和变量值存入map
void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocal get方法

 public T get() {
        //获取当前线程对象
        Thread t = Thread.currentThread();
        // 获取线程中的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            //获取ThreadLocalMap的Entry对象
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

private T setInitialValue() {
       //设置value为null
        T value = initialValue();
      //获取当前线程对象
        Thread t = Thread.currentThread();
       // 获取线程中的ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        //创建map
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
protected T initialValue() {
        return null;
    }

ThreadLocal 内存泄漏问题

在这里插入图片描述

TreadLocalMap使用ThreadLoal的弱引用为key,如果一个ThreadLocal
不存在外部强引用时,Key(ThreadLocal)势必会被 GC 回收,这样就会导致ThreadLocalMap 中 key 为 null, 而 value 还存在着强引用,无法回收,造成内存泄露。

所以每次使用完ThreadLocal都调用它的remove()放法清除数据。

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

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

相关文章

02- pandas 数据库 (机器学习)

pandas 数据库重点: pandas 的主要数据结构: Series (一维数据)与 DataFrame (二维数据)。 pd.DataFrame(data np.random.randint(0,151,size (5,3)), # 生成pandas数据 index [Danial,Brandon,softpo,Ella,Cindy], # 行索引 …

J6412四网口迷你主机折腾虚拟机教程

今天给大家做一个四网口迷你主机折腾虚拟机的安装教程&#xff0c;主机采用的是maxtang大唐NUC J6412 intel i226V四网口的迷你主机&#xff0c;这款主机它是不能直接装上NAS的&#xff0c;必须使用虚拟机系统&#xff0c;近期研究了下然后做了一个教程分享给大家。 首先需要做…

Antd-table全选踩坑记录

目录 一、需求 二、问题 ​编辑三、解决 四、全选选中所有数据而不是当前页 一、需求 最近遇到一个小小的需求&#xff0c;在我们这个项目中&#xff0c;有一个表格需要添加全选删除功能。这还不简单吗&#xff0c;于是我找到andt的官网&#xff0c;咔咔咔一顿cv&#xff0…

「自控元件及线路」14 电子电力技术与功率放大器概述

本节介绍电子电力技术的基本概念 本节介绍PD、SCR、GTR、MOSFET、IGBT等电子电力器件 本节介绍功率放大器的基本概念和线性功率放大器 文章目录电力电子技术概述电能变换电子电力器件功率二极管PD晶闸管SCR功率晶体管GTR功率场效应晶体管PowerMOSFET绝缘栅双极晶体管IGBT功率放…

代码随想录.力扣.二叉树.105/106. 从中序与前序/后序序列构造二叉树

题目&#xff1a; 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: [3,9,2…

Notion 笔记Mac及windows客户端汉化

1、 注册/登录账号&#xff1a; https://www.notion.so/zh-cn 2、下载电脑软件应用&#xff1a; https://www.notion.so/desktop 下载完成后&#xff0c;安装到电脑中&#xff0c;界面如下&#xff1a; 3、下载汉化js插件 地址&#xff1a;https://github.com/Reamd7/n…

String、StringBuffer、StringBuilder有什么区别?

第5讲 | String、StringBuffer、StringBuilder有什么区别&#xff1f; 今天我会聊聊日常使用的字符串&#xff0c;别看它似乎很简单&#xff0c;但其实字符串几乎在所有编程语言里都是个特殊的存在&#xff0c;因为不管是数量还是体积&#xff0c;字符串都是大多数应用中的重要…

【PR】源窗口

【PR】源窗口源窗口粗剪源窗口按钮功能标记按钮出入点相关插入与覆盖插入覆盖导出帧使用软件&#xff1a;Premiere2020 源窗口粗剪 我们手上可能有一些很长的视频&#xff0c;但是我就想要其中的几段&#xff0c;这个时候粗剪就很方便&#xff0c;把想要的视频段落剪出来先凑一…

私募证券基金动态-23年1月报

成交量&#xff1a;1月日均7,901.31亿元2023年1月A股两市日均成交7,901.31亿元&#xff0c;环比上升0.33%、同比下降25.18%。1月恰逢春节仅16个交易日&#xff0c;节后2个交易日交易量明显回暖。管理人&#xff1a;新提交备案51家&#xff0c;备案通过21家1月新提交备案申请的5…

分析了 200 个 DeFi 项目,我发现了这些规律

作者&#xff1a;Ren & Heinrich翻译&#xff1a;dongdong在这篇文章中&#xff0c;我分享了我通过分析当前排名前 200 的 DeFi 加密项目的见解。这不是一项学术研究。尽管如此&#xff0c;这些发现对加密货币投资者来说具有附加值。我使用 https://defillama.com/ 的公共数…

财报解读:业务复苏迹象明显,中国中免能否重写增长神话?

2月3日&#xff0c;中国中免披露2022年度业绩快报&#xff0c;2022年总营收为544.63亿元&#xff0c;同比下降19.52%&#xff1b;实现归属于上市公司股东的净利润50.25亿元&#xff0c;同比下降47.95%。来源&#xff1a;中国中免2022年度业绩快报业绩近乎腰斩&#xff0c;但从长…

库存管理系统软件哪个好用 盘点前十名!

库存管理怎么做&#xff1f;库存管理系统有什么用处&#xff1f;市面上那么多库存管理系统&#xff0c;我们又如何挑选呢&#xff1f; 本文将为您介绍库存管理系统挑选的方法以及库存管理系统的选型。 库存管理系统前十名为&#xff1a;1、简道云库存管理软件&#xff1b;2、…

网络通讯的理解

tcp/ip 协议族ip在真实环境中&#xff0c;会把主机号再分成一个子网号和一个主机号。这样的主机号才是最终容纳的主机数量。所以需要使用子网掩码&#xff08;32位&#xff09;来分子网号和主机号。其中值为1的比特是网络号和子网号&#xff0c;值为0的是比特是主机号。可以在w…

真的麻了,别再为难软件测试员了......

前言 有不少技术友在测试群里讨论&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了,考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些测试工程师了。 这不&#xff0c;为了帮大家节约时…

Nacos【一】Nacos集群部署配置

系列文章目录 暂无 文章目录系列文章目录前言一、Nacos集群架构1.ip直连2. SLB3. 域名-SLB二、集群部署准备2.1 机器准备2.2 Nginx安装配置1.安装2.负载均衡配置2.3 nacos安装配置1.nacos节点2. MySQL准备1.Docker安装MySQL2. nacos对应数据库初始化三、 集群启动1.失败原因汇…

Python深度学习实战PyQt5信号与槽的连接

本文讲解信号与槽的连接机制&#xff0c;详细示范各种类型的信号/槽连接的实现方法&#xff0c;这是图形用户界面的核心内容。还将介绍面向对象的程序设计&#xff0c;这是图形用户界面的基本思想目录1. 信号与槽&#xff08;Signals and slots&#xff09;信号与槽机制是 PyQt…

源码分析Spring解决循环依赖的过程

循环依赖是之前很爱问的一个面试题&#xff0c;最近不咋问了&#xff0c;但是梳理Spring解决循环依赖的源码&#xff0c;会让我们对Spring创建bean的流程有一个清晰的认识&#xff0c;有必要搞一搞。开始搞之前&#xff0c;先参考了这个老哥写的文章&#xff0c;对Spring处理循…

4. Spring 之 AOP

文章目录1. AOP 简介2. AOP 入门案例3. AOP 工作流程&#xff08;略&#xff09;4. AOP 切入点表达式4.1 语法格式4.2 通配符4.3 书写技巧5. AOP 通知类型5.1 前置通知、后置通知5.2 环绕通知(重点)5.3 返回后通知(了解)5.4 抛出异常后通知(了解)6. 案例&#xff1a;业务层接口…

Elasticsearch为什么快?

1、搜索引擎为什么快&#xff1f;倒排索引核心原理概括的说&#xff0c;一个优秀的搜索引擎的设计&#xff0c;至少应该具备以下几点要求&#xff1a;高效的压缩算法快速的编码和解码算法合理的数据结构通用最小化算法结合以上几点&#xff0c;后面我将通过一个案例来讲解&…

MQ消息中间件常见题及解决办法

目录儿常见MQRocketMQ2、RocketMQ测试可用MQ常见问题1、幂等性问题2、如何保证消息不丢失3、消息积压问题4、事务消息设计分析常见MQ RocketMQ RocketMQ又四部分组成 NameServer 同步Broker服务信息&#xff0c;给消费者和生产者提供可用Broker的服务信息。Broker 消息存储业…