并发编程-学习总结(下)
目录
1、Future
1.1、Callable和Runnable的不同
1.2、Future的主要功能
1.3、常用方法
1.4、Future使用注意事项
1.5、CompletableFuture(旅游平台问题)
1.5.1、需求
1.5.2、解决方案1:串行
1.5.3、解决方案2:线程池
1.5.4、解决方案3:CountDownLatch
1.5.4、解决方案4:CompletableFuture(推荐)
2、线程协作
2.1、信号量
2.2、CountDownLatch(闭锁)
2.2.1、场景一:一个线程等待多个线程
2.2.2、场景二:多个线程等待一个线程
2.3、CyclicBarrier(栅栏)
2.3.1、原理
2.3.2、构造函数
2.3.3、CyclicBarrier和CountDownLatch的区别
3、Java内存模型
3.1、Java内存模型(JMM)
3.2、指令重排序
3.3、Java原子操作的注意事项
3.3.1、Java中的原子操作
3.3.2、long和double的原子性
3.4、内存可见性
3.5、主内存和工作内存的关系
3.6、happens-before原则
3.6.1、定义
3.6.2、规则
3.7、volatile作用
3.8、单例模式的双重检查锁模式
3.8.1、实现代码
3.8.2、实体对象为什么要加volatile?
4、CAS原理
4.1、CAS是什么
4.2、优点
4.3、CAS应用场景
4.4、CAS导致的问题
5、死锁问题
5.1、定义
5.2、条件
5.3、命令行定位死锁(jstack)
5.4、如何解决死锁
5.4.1、线上死锁
5.4.2、避免死锁
5.4.3、检测与恢复策略(先允许系统发生死锁,然后再解除)
5.4.4、鸵鸟策略
6、final关键字和不变性
6.1、用法
6.2、为什么String被设计成不可变的
7、AQS框架
7.1、原理
7.2、线程协同工具类(AQS实现)
1、Future
1.1、Callable和Runnable的不同
- Callable:有返回对象(Future),可以抛出受检异常,需实现call方法。
- Runnable:无返回值,不能抛出受检异常,需实现run方法。
1.2、Future的主要功能
通过 Future 可以了解任务执行情况,或者取消任务的执行,还可获取任务执行的结果。
1.3、常用方法
- get():获取任务的执行结果。
任务已经结束:get()方法直接返回执行结果;
任务未开始或运行中:get()方法会阻塞当前线程;
任务抛出异常:get()方法会抛出ExecutionException异常;
任务被取消:get()方法会抛出CancellationException异常;
任务超时:get()方法会抛出TimeoutException异常。
- isDone():判断当前任务是否执行完毕。
成功执行和异常情况,都会返回true。
- cancle():取消任务执行。
任务还未开始:
调用cancle(),任务取消,返回true。
任务已经完成,或已经被取消:
调用cancle(),取消失败,返回false。
任务正在执行:
调用cancle(boolean mayInterruptIfRunning)
mayInterruptIfRunning = true:执行任务的线程就会收到一个中断的信号,正在执行的任务可能会有一些处理中断的逻辑,进而停止。
mayInterruptIfRunning = false:代表不中断正在运行的任务,也就是说,本次 cancel 不会有任何效果,同时 cancel 方法会返回 false。
- isCancelled:判断能否被取消。
1.4、Future使用注意事项
- 当 for 循环批量获取 Future 的结果时容易 block,get 方法调用时应使用 timeout 限制。
- 带超时参数的 get(long timeout, TimeUnit unit) 方法。如果在限定的时间内没能返回结果的话,那么便会抛出一个 TimeoutException 异常,随后就可以把这个异常捕获住,或者是再往上抛出去,这样就不会一直卡着了。
- Future 的生命周期不能后退
- Future 并没有产生新的线程。
- 在把 Callable 提交到线程池后,真正执行 Callable 的其实还是线程池中的线程,而线程池中的线程是由 ThreadFactory 产生的,这里产生的新线程与 Callable、Future 都没有关系,所以 Future 并没有产生新的线程。
1.5、CompletableFuture(旅游平台问题)
1.5.1、需求
需要获取各航空公司的机票信息,汇总后展示给用户。
1.5.2、解决方案1:串行
串行获取:耗时多,不可取。
1.5.3、解决方案2:线程池
线程池并行获取:必须等待3s。
1.5.4、解决方案3:CountDownLatch
CountDownLatch并行获取。
1.5.4、解决方案4:CompletableFuture(推荐)
2、线程协作
2.1、信号量
- 作用
控制那些需要限制并发访问量的资源。
- 原理
信号量会维护“许可证”的计数,而线程去访问共享资源前,必须先拿到许可证。线程可以从信号量中去“获取”一个许可证,一旦线程获取之后,信号量持有的许可证就转移过去了,所以信号量手中剩余的许可证要减一。
- 示例
- 主要方法
Semaphore(int permits, boolean fair):初始化许可证
第一个参数是许可证的数量,另一个参数是是否公平。
acquire():获取许可证,可响应中断。
acquireUninterruptibly(): 获取许可证,不可响应中断。
release():释放许可证。
- 注意事项
获取和释放的许可证数量尽量保持一致
信号量支持跨线程、跨线程池。
合理的情况下,可由线程A获取,线程B释放。
- 信号量能被 FixedThreadPool 替代吗?
不能。考虑如下场景:
在调用慢服务之前需要有个判断条件,比如只想在每天的零点附近去访问这个慢服务时受到最大线程数的限制(比如 3 个线程),而在除了每天零点附近的其他大部分时间,我们是希望让更多的线程去访问的。所以在这种情况下就应该把线程池的线程数量设置为 50 ,甚至更多,然后在执行之前加一个 if 判断,如果符合时间限制了(比如零点附近),再用信号量去额外限制,这样做是比较合理的。
2.2、CountDownLatch(闭锁)
CountDownLatch不能重用。
2.2.1、场景一:一个线程等待多个线程
一个线程等待其他多个线程执行完毕,才会继续往下执行。在主线程里await(),在任务线程里countDown()。
public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(5);ExecutorService service = Executors.newFixedThreadPool(5);for (int i = 0; i < 5; i++) {final int no = i + 1;Runnable runnable = new Runnable() {@Overridepublic void run() {try {Thread.sleep((long) (Math.random() * 10000));System.out.println(no + "号运动员完成了比赛。");} catch (InterruptedException e) {e.printStackTrace();} finally {latch.countDown();}}};service.submit(runnable);}System.out.println("等待5个运动员都跑完.....");latch.await();System.out.println("所有人都跑完了,比赛结束。");}
2.2.2、场景二:多个线程等待一个线程
多个线程等待某一个线程的信息,同时开始执行。在主线程countDown(),在任务线程里await()。
public static void main(String[] args) throws InterruptedException {System.out.println("运动员有5秒的准备时间");CountDownLatch countDownLatch = new CountDownLatch(1);ExecutorService service = Executors.newFixedThreadPool(5);for (int i = 0; i < 5; i++) {final int no = i + 1;Runnable runnable = new Runnable() {@Overridepublic void run() {System.out.println(no + "号运动员准备完毕,等待裁判员的发令枪");try {countDownLatch.await();System.out.println(no + "号运动员开始跑步了");} catch (InterruptedException e) {e.printStackTrace();}}};service.submit(runnable);}Thread.sleep(5000);System.out.println("5秒准备时间已过,发令枪响,比赛开始!");countDownLatch.countDown();}
2.3、CyclicBarrier(栅栏)
2.3.1、原理
内部有一个正数计数器,初始化时指定一个正数,每当有一个线程使用await()方法时,计数器加1,当前线程阻塞,直到计数器的值为初始化的正数时,打开栅栏。
2.3.2、构造函数
- CyclicBarrier(int parties)
指定一个正整数parties,在线程中调用await(),阻塞当前线程;
当有parties个线程都调用await()时,唤醒之前阻塞的所有线程,继续往下执行。
- CyclicBarrier(int parties, Runnable barrierAction)
parties:等待的线程数
barrierAction:预定数量的线程数到达后需要执行的操作
2.3.3、CyclicBarrier和CountDownLatch的区别
- 相同点
都能阻塞一个或一组线程,直到某个预设的条件达成发生,再统一出发。
- 不同点
作用对象不同:
CyclicBarrier 要等固定数量的线程都到达了栅栏位置才能继续执行,作用于线程;
CountDownLatch 只需等待数字倒数到 0,作用于事件;
CountDownLatch 是在调用了 countDown 方法之后把数字倒数减 1,而 CyclicBarrier 是在某线程开始等待后把计数减 1。
可重用性不同:
CountDownLatch不可重用,锁打开后就不能重用了;
CyclicBarrier可重用,满足线程数量后可自动重新计数,也可调用reset方法进行重置CyclicBarrier。
执行动作不同:
CyclicBarrier 有执行动作 barrierAction,而 CountDownLatch 没这个功能。
3、Java内存模型
3.1、Java内存模型(JMM)
- JVM内存模型
和Java虚拟机的运行时区域有关。
程序计数器、Java堆、虚拟机栈、方法区、运行时常量池。
.java文件->编译(.class文件)->机器指令->CPU运行机器指令
- Java内存模型(JMM)
和Java的并发编程有关。
JMM 是一组规范,保证同一个程序在不同虚拟机上运行可得到相同的结果。
实现java程序在各种不同的平台上都能达到内存访问的一致性。
重排序、原子性、内存可见性。
3.2、指令重排序
在保证业务逻辑不变的前提下,对指令执行的顺序进行调整,进而提高处理速度。
重排序的时机:
编译器优化、CPU重排序、内存重排序
3.3、Java原子操作的注意事项
3.3.1、Java中的原子操作
- 除了 long 和 double 之外的基本类型(int、byte、boolean、short、char、float)的读/写操作,都天然的具备原子性
- 所有引用 reference 的读/写操作
- 加了 volatile 后,所有变量的读/写操作(包含 long 和 double)。这也就意味着 long 和 double 加了 volatile 关键字之后,对它们的读写操作同样具备原子性
- volatile修饰的变量只能保证读/写的原子性,不能保证组合操作的原子性。
- 在 java.concurrent.Atomic 包中的一部分类的一部分方法是具备原子性的,比如 AtomicInteger 的 incrementAndGet 方法。
3.3.2、long和double的原子性
long 和 double 的值需要占用 64 位的内存空间,而对于 64 位值的写入,可以分为两个 32 位的操作来进行。
而在目前各种平台下的主流虚拟机的实现中,几乎都会把 64 位数据的读写操作作为原子操作来对待,因此我们在编写代码时一般不需要为了避免读到“半个变量”而把 long 和 double 声明为 volatile 的。
3.4、内存可见性
工作内存保存的是主内存的副本。
线程变量更改后,不能立即更新到主内存,导致其他线程不能及时获取最新的数据。
3.5、主内存和工作内存的关系
- 所有的变量都存储在主内存中,同时每个线程拥有自己独立的工作内存,而工作内存中的变量的内容是主内存中该变量的拷贝
- 线程不能直接读 / 写主内存中的变量,但可以操作自己工作内存中的变量,然后再同步到主内存中,这样,其他线程就可以看到本次修改
- 主内存是由多个线程所共享的,但线程间不共享各自的工作内存,如果线程间需要通信,则必须借助主内存中转来完成
3.6、happens-before原则
3.6.1、定义
如果第一个操作 happens-before 第二个操作,那么我们就说第一个操作对于第二个操作一定是可见的,也就是第二个操作在执行时就一定能保证看见第一个操作执行的结果。
如果有操作 x 和操作 y,用 hb(x, y) 来表示 x happens-before y。
3.6.2、规则
- 锁操作的 happens-before 规则
操作 A 是解锁,而操作 B 是对同一个锁的加锁,那么 hb(A, B) 。
线程 A 在解锁之前的所有操作,对于线程 B 的对同一个锁的加锁之后的所有操作而言,都是可见的。
- volatile 的 happens-before 规则
对一个 volatile 变量的写操作 happen-before 后面对该变量的读操作。
如果变量被 volatile 修饰,那么每次修改之后,其他线程在读取这个变量的时候一定能读取到该变量最新的值。
3.7、volatile作用
保证可见性
volatile修饰的变量直接写入到主内存中,不存在主内存和工作内存同步造成的数据延迟。
禁止重排序
由于编译器或 CPU 的优化,代码的实际执行顺序可能与我们编写的顺序是不同的,这在单线程的情况下是没问题的,但是一旦引入多线程,这种乱序就可能会导致严重的线程安全问题。
3.8、单例模式的双重检查锁模式
3.8.1、实现代码
public class Singleton {private static volatile Singleton singleton;private Singleton() {}public static Singleton getInstance() {if (singleton == null) {synchronized (Singleton.class) {if (singleton == null) {singleton = new Singleton();}}}return singleton;}
}
3.8.2、实体对象为什么要加volatile?
- 不加volatile时可能发生指令重排序,顺序如上图所示;
- 在第二步时,对象就不为null了;
- 此时,另一个线程进行第一个ifnull判断,不为空,返回对象;
- 但此时的对象还没有进行初始化,在使用时就可能发生问题。
4、CAS原理
4.1、CAS是什么
Compare-And-Swap:比较并交换,是乐观锁的底层原理。避免使用互斥锁。
CAS 有三个操作数:内存值 V、预期值 A、要修改的值 B。CAS 最核心的思路就是,仅当预期值 A 和当前的内存值 V 相同时,才将内存值修改为 B。
4.2、优点
- 当多个线程同时使用 CAS 更新同一个变量时,只有其中一个线程能够操作成功,而其他线程都会更新失败,但更新失败的线程并不会被阻塞,而是被告知这次由于竞争而导致的操作失败,但还可以再次尝试;
- 原子性: CAS 相关的指令是具备原子性的,这个组合操作在执行期间不会被打断,这样就能保证并发安全。
4.3、CAS应用场景
- 并发容器:ConcurrentHashMap、ConcurrentLinkedQueue
- 数据库:更新前检查版本号:
在更新数据时,我们可以利用 version 字段在数据库中实现乐观锁和 CAS 操作,而在获取和修改数据时都不需要加悲观锁
- 原子类:Unsafe类
4.4、CAS导致的问题
- ABA问题(可以通过添加版本号解决)
假设有两个线程——线程1和线程2,两个线程按照顺序进行以下操作:
线程1读取内存中数据为A;
线程2将该数据修改为B;
线程2将该数据修改为A;
线程1对数据进行CAS操作
第4步中的A已经不是第1步中的A值了。
比如栈顶问题->一个栈的栈顶经过两次(或多次)变化又恢复了原值,但是栈可能已发生了变化
- 高竞争下的开销问题
在并发冲突概率大的高竞争环境下,CAS一直失败,会一直重试,导致CPU开销较大。
解决方法:增加重试阈值
5、死锁问题
5.1、定义
死锁就是两个或多个线程(或进程)被无限期地阻塞,相互等待对方手中资源的一种状态。
5.2、条件
- 互斥
共享资源,同一时间只能被一个线程使用。
- 请求与条件保持
当一个线程因请求资源而阻塞时,则需对已获得的资源保持不放。如果在请求资源时阻塞了,并且会自动释放手中资源(例如锁)的话,那别人自然就能拿到我刚才释放的资源,也就不会形成死锁。
- 不剥夺条件
线程已获得的资源,在未使用完之前,不会被强行剥夺
- 循环等待条件
多个线程,分别持有对方所需的资源,并形成环路。
比如:
线程1:持有锁A,尝试获取锁B;
线程2:持有锁B,尝试获取锁C;
线程3:持有锁C,尝试获取锁A。
5.3、命令行定位死锁(jstack)
- 查询当前Java程序的pid:${JAVA_HOME}/bin/jps
- 获取线程获取锁的信息:${JAVA_HOME}/bin/jstack pid
5.4、如何解决死锁
5.4.1、线上死锁
立刻保存JVM信息、日志信息,然后立刻重启服务。
5.4.2、避免死锁
- 顺序加锁,锁嵌套时,保证多个Lock以相同的顺序请求加锁
- 超时放弃,Lock接口的tryLock(long time, TimeUnit unit),按照固定时长等待锁,获取锁超时后,主动释放已经获得的锁
- 尽量避免同一个线程对多个Lock进行锁定
5.4.3、检测与恢复策略(先允许系统发生死锁,然后再解除)
- 终止线程
线程优先级:先终止优先级低的线程;
占用资源:先终止占用资源少的线程;
运行时间:先终止运行时间少的线程。
- 资源抢占
剥夺某个线程已经获取的资源,供其他线程使用。(线程回退、释放资源)
5.4.4、鸵鸟策略
死锁发生概率极小时,允许死锁发生,发生后再人工处理死锁,比如重启服务等。
6、final关键字和不变性
6.1、用法
- 修饰变量
该变量一旦被赋值就不能修改
成员变量:声明变量时直接赋值;构造函数中赋值、类的构造代码块中赋值。
静态变量:声明变量时直接赋值;静态代码块中赋值。
局部变量:在使用前进行赋值即可。
修饰入参
在方法内部不能修改该参数
修饰对象时,该对象的引用不可变,该对象的内容是可变的。
- 修饰方法:被final修饰的方法不能被重写,即不能被override
- 修饰类:该类不能被继承
6.2、为什么String被设计成不可变的
- 节省大量内存空间
使用字符串常量池,两个字符串变量内容一致,就会指向同一个对象,而不需要创建新对象。
- 保证hash值的唯一性
用作HashMap的key,保证对同一个String进行hash能获得相同的hash值。
- 避免重复计算hash值:
String中有个hash成员变量,用以缓存hashcode,不用每次都计算hash值。
- 线程安全
7、AQS框架
是一个用于构建锁、同步器等线程协作工具类的框架。
应用:ReentrantLock、ReentrantReadWriteLock、Semaphore(信号量-许可证)、CountDownLatch(闭锁)
7.1、原理
三大核心:状态(status)、队列、期望协作工具类去实现的获取/释放等重要方法。
State变量可以理解成同步资源的锁状态,值为0时表示当前资源未被线程占用,值不为0时表示当前共享资源已被线程获取锁。
AQS就是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。
AQS是一个多线程访问同步资源的的同步器框架。该框架维护了一个volatile int state(共享资源)和一个FIFO(CLH双向队列)线程等待队列(多线程争夺共享资源时会进入此队列)。
AQS一般以继承的方式被使用,重入锁ReentrantLock、闭锁、栅栏等都用到了AQS。
AQS的实现依赖内部的同步队列(FIFO双向队列),如果当前线程获取同步状态失败,AQS会将当前线程以及等待状态等信息构造成一个Node,将其加入同步队列的尾部,同时阻塞当前线程,当同步状态释放时,唤醒队列的头节点。
- 状态(status)
一个volatile int state(共享资源)。表示线程协作工具类的业务逻辑和状态。
信号量里表示许可证的数量
CountDownLatch里表示倒数的数量
ReentrantLock中表示锁的占有情况
CAS乐观锁实现:通过compareAndSetState 及 setState两个方法修改status的值。
- FIFO队列
存储等待的线程。充当线程的“排队管理器”。
如果当前线程获取同步状态失败,AQS会将该线程以及等待状态等信息构造成一个Node,将其加入同步队列的尾部,同时阻塞当前线程,当同步状态释放时,唤醒队列的头节点。
- 获取/释放方法
获取方法
ReentrantLock 中的 lock 方法,status=0获取成功;
Semaphore 中的 acquire 方法,status=正数获取成功;
CountDownLatch 获取方法就是 await 方法,status=0获取成功。
释放方法
ReentrantLock的unLock方法;
Semaphore的release方法;
CountDownLatch 的countDown方法。
7.2、线程协同工具类(AQS实现)
实现类只需要实现共享资源state的获取和释放方式即可。
独占:只有一个线程能执行->ReentrantLock
共享:多个线程可以同时执行->Semaphore、CountDownLatch、CyclicBarrier
独占和共享:ReentrantReadWriteLock
以上内容为个人学习理解,如有问题,欢迎在评论区指出。
部分内容截取自网络,如有侵权,联系作者删除。
相关文章:

并发编程-学习总结(下)
目录 1、Future 1.1、Callable和Runnable的不同 1.2、Future的主要功能 1.3、常用方法 1.4、Future使用注意事项 1.5、CompletableFuture(旅游平台问题) 1.5.1、需求 1.5.2、解决方案1:串行 1.5.3、解决方案2:线程池 1.5.4、解决方案3…...

arm汇编指令详细整理及实例详解
目录一、简介二、ARM 汇编指令说明2.1 32位数据操作指令2.2 32位存储器数据传送指令2.3 32位转移指令2.4 其它32位指令三、实例讲解3.1 MRS3.2 MSR3.3 PRIMASK3.4 FAULTMASK3.5 BX指令3.6 零寄存器 wzr、xzr3.7 立即寻址指令3.8 寄存器间接寻址指令3.9 寄存器移位寻址指令3.10 …...

高等数学笔记(下下)
无穷级数 定义 一般的,如果给定一个数列u1,u2,u3,...un,...,u_1, u_2, u_3, ... u_n, ... ,u1,u2,u3,...un,...,,那么由这个梳理构成的表达式u1u2u3...un...u_1u_2u_3...u_n...u1u2u3...un...叫做(常数项)无穷级数,简称(常…...

零基础如何入门网络安全(黑客)
我经常会看到这一类的问题: 学习XXX知识没效果;学习XXX技能没方向;学习XXX没办法入门; 给大家一个忠告,如果你完全没有基础的话,前期最好不要盲目去找资料学习,因为大部分人把资料收集好之后&a…...

【C++】map和set用法详解
文章目录1.关联式容器2.键值对3.树形结构的关联式容器3.1 set3.1.1 set的介绍3.1.2 set的模板参数列表3.1.3 set的使用3.2 mapmap的介绍map的模板参数列表map的使用关于map的元素访问总结3.3multimap1.关联式容器 我们接触过STL中的部分容器,比如:vecto…...

BLIP2-图像文本预训练
文章目录摘要解决问题算法模型结构通过frozen图像编码器学习视觉语言表征图像文本对比学习(ITC)基于图像文本生成(ITG)图文匹配(ITM)从大规模语言模型学习视觉到语言生成模型预训练预训练数据预训练图像编码…...

Faster-Rcnn修改转数据集文件
目录 学习python的一些基础知识 argparser assert关键字 让你秒懂Python 类特殊方法__getitem__ lxml.etree.fromstring的使用 统计一下json文件内的种类 正脸红外光 正脸-混合红外光 正脸-交叉偏振光 正脸-平行偏振光 正脸-紫外光 正脸-棕色光 调用mydataset可视化…...

带你沉浸式体验删库跑路
前言:学习的过程比较枯燥,后面会记录一些比较有意思的东西,比如程序员之间流传的删库跑路的梗,当然本次测试是在虚拟机上进行的并进行了快照保护,所以其实没太大问题。首先得要有一个虚拟机要有一个linux iso文件装在虚拟机上以上两点不是本文重点,如果有需要可以私…...

Linux学习(8.5)文件内容查阅
目录 文件内容查阅: 直接检视文件内容 cat (concatenate) tac (反向列示) nl (添加行号列印) 可翻页检视 more (一页一页翻动) less (一页一页翻动) 数据撷取 tail (取出后面几行) 非纯文字档: od 修改文件时间或建置新档: touc…...

【Docker】命令总结
目录 1.镜像命令 1.1拉取镜像 1.2查看镜像 1.3保存镜像 1.4导入镜像 2.容器命令 2.1创建并运行容器 2.2删除容器 2.3进入容器 2.4查看容器状态 2.5暂停容器 2.6恢复容器 2.7停止容器 2.8启动容器 2.8查看容器日志 3.数据卷命令 3.1创建数据卷 3.2查看所有数据…...

并发编程-学习总结(上)
目录 1、线程基础 1.1、线程实现方法 1.2、如何正确停止线程 1.3、Java线程的六种状态 1.4、wait/notify/notifyAll注意事项 1.4.1、为什么 wait 、notify、notifyAll必须在 synchronized 保护的同步代码中使用? 1.4.2、为什么 wait/notify/notifyAll 被定义…...

QT之OpenGL混合
QT之OpenGL混合1. 概述2. 实现2.1 丢弃片段2.1.1 Demo2.2 混合2.2.1 相关函数2.2.2 排序问题2.2.3 Demo1. 概述 OpenGL中,混合(Blending)通常是实现物体透明度(Transparency)的一种技术。 2. 实现 2.1 丢弃片段 在某些情况下,有些片段是只需要设置显…...

【1255. 得分最高的单词集合】
来源:力扣(LeetCode) 描述: 你将会得到一份单词表 words,一个字母表 letters (可能会有重复字母),以及每个字母对应的得分情况表 score。 请你帮忙计算玩家在单词拼写游戏中所能获…...

nginx模块介绍
新编译前,在对应的nginx原编译文件夹 如:nginx-1.23.0 下,要 make clean 清空以前编译的objs文件夹,实际上就是执行了rm objs文件夹。 很多要用到git,先yum install git -y echo-nginx-module 让nginx直接使用echo的…...

排错工具ping和trace(电子科技大学TCP/IP实验四)
一.实验目的 1、了解网络连通性测试的方法和工作原理 2、了解网络路径跟踪的方法和工作原理 3、掌握 MTU 的概念和 IP 分片操作 4、掌握 IP 分组生存时间(TTL)的含义和作用 5、掌握路由表的作用和路由查找算法 二.预备知识 …...

node.js中ws模块创建服务端和客户端
一、WebSocket出现的原因 1、Http协议发布REST API 的不足: 每次请求响应完成之后,服务器与客户端之间的连接就断开了,如果客户端想要继续获取服务器的消息,必须再次向服务器发起请 求。这显然无法适应对实时通信有高要求的场景…...

kubernates-1.26.1 kubeadm containerd 单机部署
k8s1.26 kubeadm containerd 安装 kubeadm init 时提示 containerd 错误 failed to pull image “k8s.gcr.io/pause:3.6” 报错日志显示containerd pull时找不到对应的pause版本,而不是registry.k8s.io/pause:3.9 [rootk8s-master containerd]# kubeadm init --k…...

如何在 iPhone 上恢复已删除的通话记录/通话记录
您的通话记录/通话记录可能很重要,尤其是当您想要拨打之前联系过但未保存的号码时。如果您碰巧删除了通话记录(有意或无意),本指南将帮助您了解如何检索它们并找回您需要使用的所有记录。我们将根据您的情况和您拥有的工具讨论不同…...

Canonical为所有支持的Ubuntu LTS系统发布了新的Linux内核更新
导读Canonical近日为所有支持的Ubuntu LTS系统发布了新的Linux内核更新,以解决总共19个安全漏洞。新的Ubuntu内核更新仅适用于长期支持的Ubuntu系统,包括Ubuntu 22.04 LTS(Jammy Jellyfish)、Ubuntu 20.04 LTS(Focal F…...

MS9122是一款USB单芯片投屏器,内部集成了USB2 0 控制器和数据收发模块、HDMI 数据接口和音视频处理模块。MS9122可以通过USB接口显示
MS9122是一款USB单芯片投屏器,内部集成了USB2.0 控制器和数据收发模块、HDMI 数据接口和音视频处理模块。MS9122可以通过USB接口显示或者扩展PC、智能手机、平板电脑的显示信息到更大尺寸的显示设备,支持HDMI视频接口。 主要功能特征 HDMI v1.4兼容 最大…...

C++学习笔记-数据抽象
简单的说,数据抽象是用来描述数据结构的。数据抽象就是 ADT。一个 ADT 主要表现为它支持的一些操作,比方说 stack.push、stack.pop,这些操作应该具有明确的时间和空间复杂度。另外,一个 ADT 可以隐藏其实现细节,比方说…...

【Android】Android开发笔记(一)
【Android】Android开发笔记(一) 在Android Studio中import module和delete moduleimport moduledelete moduleAndroid Studio中App(Module)无法正常运行在实机上测试App一些基本概念App的工程结构结语在Android Studio中import m…...

C语言数据结构(二)—— 受限线性表 【栈(Stack)、队列(Queue)】
在数据结构逻辑层次上细分,线性表可分为一般线性表和受限线性表。一般线性表也就是我们通常所说的“线性表”,可以自由的删除或添加结点。受限线性表主要包括栈和队列,受限表示对结点的操作受限制。一般线性表详解,请参考文章&…...

线程安全之synchronized和volatile
目录 1.线程不安全的原因 2.synchronized和volatile 2.1 synchronized 2.1.1 synchornized的特性 2.1.2 synchronized使用示例 2.2 volatile 我们先来看一段代码: 分析以上代码,t1和t2这两个线程的任务都是分别将count这个变量自增5000次ÿ…...

量子计算对网络安全的影响
量子计算的快速发展,例如 IBM 的 Quantum Condor 处理器具有 1000 个量子比特的容量,促使专家们宣称第四次工业革命即将实现“量子飞跃”。 量子计算机的指数处理能力已经受到政府和企业的欢迎。 由于从学术和物理原理到商业可用解决方案的不断转变&am…...

MyBatis——增删改查操作的实现
开启mybatis sql日志打印 可以在日志中看到sql中执行的语句 在配置文件中加上下面这几条语句 mybatis.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl logging.level.com.example.demodebug查询操作 根据用户id查询用户 UserMapper: User…...

【7】linux命令每日分享——cat查看文件内容
大家好,这里是sdust-vrlab,Linux是一种免费使用和自由传播的类UNIX操作系统,Linux的基本思想有两点:一切都是文件;每个文件都有确定的用途;linux涉及到IT行业的方方面面,在我们日常的学习中&…...

新氧2023年财务业绩预测:退市风险大幅降低,收入增长将放缓
来源:猛兽财经 作者:猛兽财经 公司进展 与新氧(SY)有关的两个重要积极进展值得一提。 第一个积极进展是新氧的退市风险已在很大程度上降低。 2023年1月6日,新氧披露,它已经“重新符合纳斯达克规定的股价每…...

C++使用shared_ptr与weak_ptr轻松管理内存
智能指针之shared_ptr与weak_ptr前言智能指针实例分析前言 C与其他语言的不同点之一就是可以直接操作内存,这是一把双刃剑,直接操作内存可以提高开发的灵活度,开发人员在合适的时机申请内存,在合适的时机释放内存,减少…...

Buuctf reverse [FlareOn4]IgniteMe 题解
一. 查壳 无壳32位程序 二. ida打开 GetStdHandle函数根据微软官方文档可以得知是获取标准输入/输出/错误的句柄 参数里的 0xFFFFFFF6转换一下是4294967286, 对应(DWORD) -10 所以这里的WriteFile函数实际上是实现了printf的功能 sub_4010F0()函数 其功能是通过ReadFile函数读取…...