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

并发线程、锁、ThreadLocal

并发编程

  • 并发编程
      • 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());elseacquire(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>(){@Overrideprotected Integer initialValue() {return 0;}};

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

ThreadLocal set方法

 //set 方法public void set(T value) {//获取当前线程对象Thread t = Thread.currentThread();//判断该对象是否已经存入mapThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}
// 获取 threadLocalmapThreadLocalMap 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为nullT value = initialValue();//获取当前线程对象Thread t = Thread.currentThread();// 获取线程中的ThreadLocalMap对象ThreadLocalMap map = getMap(t);//创建mapif (map != null)map.set(this, value);elsecreateMap(t, value);return value;}
protected T initialValue() {return null;}

ThreadLocal 内存泄漏问题

在这里插入图片描述

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

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

相关文章:

并发线程、锁、ThreadLocal

并发编程并发编程Java内存模型&#xff08;JMM&#xff09;并发编程核心问题—可见性、原子性、有序性volatile关键字原子性原子类CAS(Compare-And-Swap 比较并交换)ABA问题Java中的锁乐观锁和悲观锁可重入锁读写锁分段锁自旋锁共享锁/独占锁公平锁/非公平锁偏向锁/轻量级锁/重…...

CMMI-结项管理

结项管理&#xff08;ProjectClosing Management, PCM&#xff09;是指在项目开发工作结束后&#xff0c;对项目的有形资产和无形资产进行清算&#xff1b;对项目进行综合评估&#xff1b;总结经验教训等。结项管理过程域是SPP模型的重要组成部分。本规范阐述了结项管理的规程&…...

网络通信协议是什么?

网络通信基本模式 常见的通信模式有如下2种形式&#xff1a;Client-Server(CS) 、 Browser/Server(BS) 实现网络编程关键的三要素 IP地址&#xff1a;设备在网络中的地址&#xff0c;是唯一的标识。 端口&#xff1a;应用程序在设备中唯一的标识。 协议: 数据在网络中传输的…...

阶段5:Java分布式与微服务实战

目录 第33-34周 Spring Cloud电商实战 一、Eureka-server模块开发 1、引入依赖 2、配置文件 3、启动注解 一、Eureka-server模块开发 第33-34周 Spring Cloud电商实战 一、Eureka-server模块开发 1、引入依赖 父项目依赖&#xff1a;cloud-mall-practice springboot的…...

我的创作纪念日

目录 机缘 收获 日常 憧憬 机缘 其实本来从大一上学期后半段(2017)就开始谢谢零星的博客&#xff0c;只不过当时是自己用hexo搭建了一个小网站&#xff0c;还整了个域名&#xff1a;jiayoudangdang.top&#xff0c;虽然这个早就过期&#xff1b; 后来发现了CSDN&#xff…...

Qml学习——动态加载控件

最近在学习Qml&#xff0c;但对Qml的各种用法都不太熟悉&#xff0c;总是会搞忘&#xff0c;所以写几篇文章对学习过程中的遇到的东西做一个记录。 学习参考视频&#xff1a;https://www.bilibili.com/video/BV1Ay4y1W7xd?p1&vd_source0b527ff208c63f0b1150450fd7023fd8 目…...

设计模式之职责链模式

什么是职责链模式 职责链模式是避免请求发送者与接受者耦合在一起&#xff0c;让多个对象都可以接受到请求&#xff0c;从而将这些对象连接成一条链&#xff0c;并且沿着这条链传递请求&#xff0c;直到有对象处理为止。     职责链模式包含以下几个角色&#xff1a;    …...

MySQL入门篇-MySQL 8.0 延迟复制

备注:测试数据库版本为MySQL 8.0 这个blog我们来聊聊MySQL 延迟复制 概述 MySQL的复制一般都很快&#xff0c;虽然有时候因为 网络原因、大事务等原因造成延迟&#xff0c;但是这个无法人为控制。 生产中可能会存在主库误操作&#xff0c;导致数据被删除了&#xff0c;Oracl…...

FPGA时序约束与分析 --- 实例教程(1)

注意&#xff1a; 时序约束辅助工具或者相关的TCL命令&#xff0c;都必须在 open synthesis design / open implemention design 后才能有效运行。 1、时序约束辅助工具 2、查看相关时序信息 3、一般的时序约束顺序 1、 时序约束辅助工具&#xff08;1&#xff09;时序约束编辑…...

go深拷贝和浅拷贝

1、深拷贝&#xff08;Deep Copy&#xff09;拷贝的是数据本身&#xff0c;创造一个样的新对象&#xff0c;新创建的对象与原对象不共享内存&#xff0c;新创建的对象在内存中开辟一个新的内存地址&#xff0c;新对象值修改时不会影响原对象值。既然内存地址不同&#xff0c;释…...

linux网络系统层面的配置、管理及操作命令汇总

前几篇文章一一介绍了LINUX进程管理控制命令&#xff0c;关于linux系统中的软件包管理内容等&#xff0c;作为一名运维工程师&#xff0c;前两天刚处理了一起linux网络层面的情况&#xff0c;那么今天这篇文章就以linux网络层面为主题吧。当说到linux网络系统层面&#xff0c;e…...

R数据分析:孟德尔随机化中介的原理和实操

中介本身就是回归&#xff0c;基本上我看到的很多的调查性研究中在中介分析的方法部分都不会去提混杂&#xff0c;都是默认一个三角形画好&#xff0c;中介关系就算过去了&#xff0c;这里面默认的逻辑就是前两步回归中的混杂是一样的&#xff0c;计算中介效应的时候就自动消掉…...

【C++】 类和对象 (下)

文章目录&#x1f4d5;再谈构造函数1. 构造函数体赋值2. 初始化列表3. explicit 关键字&#x1f4d5;static 成员1. 概念2. static 成员变量3. static 成员函数&#x1f4d5; 友元1. 友元函数2. 友元类&#x1f4d5;内部类&#x1f4d5;编译器优化&#x1f4d5;再谈构造函数 1…...

asp获取毫秒时间戳的方法 asp获取13位时间戳的方案

一、背景。时间戳就是计算当前与"1970-01-01 08:00:00"的时间差&#xff0c;在asp中通常是使用Datediff函数来计算两个日期差&#xff0c;代码&#xff1a;timestamp Datediff("s", "1970-01-01 08:00:00",now)返回结果&#xff1a;1675951060可…...

Python基础篇(十五)-- Python程序接入MySQL数据库

程序运行时&#xff0c;数据都在内存中&#xff0c;程序终止时&#xff0c;需要将数据保存到磁盘上。为了便于程序保存和读取&#xff0c;并能直接通过条件快速查询到指定数据&#xff0c;数据库(Database)应运而生&#xff0c;本篇主要学习使用Python操作数据库&#xff0c;在…...

程序员不得不知道的 API 接口常识

说实话&#xff0c;我非常希望自己能早点看到本篇文章&#xff0c;大学那个时候懵懵懂懂&#xff0c;跟着网上的免费教程做了一个购物商城就屁颠屁颠往简历上写。 至今我仍清晰地记得&#xff0c;那个电商教程是怎么定义接口的&#xff1a; 管它是增加、修改、删除、带参查询…...

【项目精选】基于Java的银行排号系统的设计与实现

银行排号系统是为解决一些服务业营业大厅排队问题而设计的&#xff0c;它能够有效地提高工作人员的工作效率&#xff0c;也能够使顾客合理的安排等待时间&#xff0c;让顾客感到服务的公平公正。论文首先讨论了排号系统的背景、意义、应用现状以及研究与开发现状。本文在对C/S架…...

前端 基于 vue-simple-uploader 实现大文件断点续传和分片上传

文章目录一、前言二、后端部分新建Maven 项目后端pom.xml配置文件 application.ymlHttpStatus.javaAjaxResult.javaCommonConstant.javaWebConfig.javaCheckChunkVO.javaBackChunk.javaBackFileList.javaBackChunkMapper.javaBackFileListMapper.javaBackFileListMapper.xmlBac…...

解决报错: ERR! code 128npm ERR! An unknown git error occurred

在github下载的项目运行时&#xff0c;进行npm install安装依赖时&#xff0c;出现如下错误&#xff1a;npm ERR! code 128npm ERR! An unknown git error occurrednpm ERR! command git --no-replace-objects ls-remote ssh://gitgithub.com/nhn/raphael.gitnpm ERR! gitgithu…...

聊城高新技术企业认定7项需要注意的问题 山东同邦科技分享

聊城高新技术企业认定7项需要注意的问题 山东同邦科技分享 山东省高新技术企业认定办公室发布《关于开展2021年度本市高新技术企业认定管理工作的通知》&#xff0c;高企认定中有哪些问题需要注意呢&#xff1f;下面我们一起来看一下。 一、知识产权 知识产权数量和质量双达…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代&#xff0c;海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构&#xff0c;在处理大规模数据抓取任务时展现出强大的能力。然而&#xff0c;随着业务规模的不断扩大和数据抓取需求的日益复杂&#xff0c;传统…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...