Java共享问题 、synchronized 线程安全分析、Monitor、wait/notify

文章目录

  • 1.共享带来的问题
    • 1.1 临界区 Critical Section
    • 1.2 竞态条件 Race Condition
  • 2. synchronized语法及理解
    • 2.1 方法上的 synchronized
  • 3.变量的线程安全分析
    • 3.1.成员变量和静态变量是否线程安全?
    • 3.2.局部变量是否线程安全?
      • 3.2.1 局部变量线程安全分析
  • 4.Monitor
    • 4.1 Java 对象头
    • 4.2 Monitor概念
  • 5.synchronized原理
    • 5.1 轻量级锁
    • 5.2 锁膨胀
    • 5.3 自旋优化
    • 5.4 偏向锁
      • 5.4.1 偏向状态
      • 5.4.2 撤销偏向锁
        • 5.4.2.1 撤销-调用对象hashCode
        • 5.4.2.3 撤销-其他线程使用对象
        • 5.4.2.4 撤销- 调用wait/notify
      • 5.4.3 批量重偏向
      • 5.4.4 批量撤销
      • 5.4.5 锁消除
      • 5.4.6 锁粗化
  • 6.wait/notify
    • 6.1 wait/notify原理
    • 6.2 API介绍
    • 6.3 wait、notify 的正确使用

1.共享带来的问题

(1)两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果是 0 吗?

static int counter = 0;
public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 5000; i++) {counter++;}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 5000; i++) {counter--;}}, "t2");t1.start();t2.start();t1.join();t2.join();log.debug("{}",counter);
}

(2)以上的结果可能是正数、负数、零。为什么呢?因为 Java 中对静态变量的自增,自减并不是原子操作,要彻底理解,必须从字节码来进行分析
例如:
①对于 i++ 而言(i 为静态变量),实际会产生如下的 JVM 字节码指令:

getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
iadd // 自增
putstatic i // 将修改后的值存入静态变量i

②而对应 i-- 也是类似:

getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
isub // 自减
putstatic i // 将修改后的值存入静态变量i

(3)如果是单线程以上 8 行代码是顺序执行(不会交错)没有问题:

在这里插入图片描述

(4)出现负数的情况
在这里插入图片描述

(5)出现正数的情况:

在这里插入图片描述

1.1 临界区 Critical Section

(1)一个程序运行多个线程本身是没有问题的
(2)问题出在多个线程访问共享资源
①多个线程读共享资源其实也没有问题
②在多个线程对共享资源读写操作时发生指令交错,就会出现问题
(3)一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
(4)例如,下面代码中的临界区

static int counter = 0;
public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 5000; i++)// 临界区{counter++;}}, "t1");Thread t2 = new Thread(() -> {for (int i = 0; i < 5000; i++)// 临界区{counter--;}}, "t2");t1.start();t2.start();t1.join();t2.join();log.debug("{}",counter);
}

1.2 竞态条件 Race Condition

多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件。为了避免临界区的竞态条件发生,有多种手段可以达到目的。
(1)阻塞式的解决方案:synchronized,Lock
(2)非阻塞式的解决方案:原子变量
(3)synchronized,即俗称的【对象锁】,它采用互斥的方式让同一时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁的线程可以安全的执行临界区内的代码,不用担心线程上下文切换
(4)虽然 java 中互斥和同步都可以采用 synchronized 关键字来完成,但它们还是有区别的:
①互斥是保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码
②同步是由于线程执行的先后、顺序不同、需要一个线程等待其它线程运行到某个点

2. synchronized语法及理解

(1)语法

synchronized(对象) // 线程1, 线程2(blocked)
{临界区
}

(2)理解
在这里插入图片描述
①synchronized(对象) 中的对象,可以想象为一个房间(room),有唯一入口(门)房间只能一次进入一人进行计算,线程 t1,t2 想象成两个人
②当线程 t1 执行到 synchronized(room) 时就好比 t1 进入了这个房间,并锁住了门拿走了钥匙,在门内执行count++ 代码
②这时候如果 t2 也运行到了 synchronized(room) 时,它发现门被锁住了,只能在门外等待,自身发生了上下文切换,由运行阶段变为阻塞状态
③这中间即使 t1 的 cpu 时间片不幸用完,被踢出了门外(不要错误理解为锁住了对象就能一直执行下去哦),这时门还是锁住的,t1 仍拿着钥匙,t2 线程还在阻塞状态进不来,只有下次轮到 t1 自己再次获得时间片时才能开门进入
④当 t1 执行完 synchronized{} 块内的代码,这时候才会从 obj 房间出来并解开门上的锁,唤醒 t2 线程把钥匙给他。t2 线程这时才可以进入 obj 房间,锁住了门拿上钥匙,执行它的 count-- 代码

2.1 方法上的 synchronized

class Test{public synchronized void test() {}
}
等价于
class Test{public void test() {synchronized(this) {}}
}
class Test{public synchronized static void test() {}
}
等价于
class Test{public static void test() {synchronized(Test.class) {}}
}

3.变量的线程安全分析

3.1.成员变量和静态变量是否线程安全?

(1)如果它们没有共享,则线程安全
(2)如果它们被共享了,根据它们的状态是否能够改变,又分两种情况
①如果只有读操作,则线程安全
②如果有读写操作,则这段代码是临界区,需要考虑线程安全

3.2.局部变量是否线程安全?

(1)局部变量是线程安全的
(2)但局部变量引用的对象则未必
①如果该对象没有逃离方法的作用访问,它是线程安全的
②如果该对象逃离方法的作用范围,需要考虑线程安全

3.2.1 局部变量线程安全分析

public static void test1() {int i = 10;i++;
}

(1)每个线程调用 test1() 方法时局部变量 i,会在每个线程的栈帧内存中被创建多份,因此不存在共享。如图:
在这里插入图片描述
(2)局部变量引用的对象则稍有不同
①先看一个成员变量的例子

class ThreadUnsafe {ArrayList<String> list = new ArrayList<>();public void method1(int loopNumber) {for (int i = 0; i < loopNumber; i++) {// { 临界区, 会产生竞态条件method2();method3();// } 临界区}}private void method2() {list.add("1");}private void method3() {list.remove(0);}
}

执行
其中一种情况是,如果线程2 还未 add,线程1 remove 就会报错:

Exception in thread "Thread1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:657) at java.util.ArrayList.remove(ArrayList.java:496) at cn.itcast.n6.ThreadUnsafe.method3(TestThreadSafe.java:35) at cn.itcast.n6.ThreadUnsafe.method1(TestThreadSafe.java:26) at cn.itcast.n6.TestThreadSafe.lambda$main$0(TestThreadSafe.java:14) at java.lang.Thread.run(Thread.java:748) 

分析:
无论哪个线程中的 method2 引用的都是同一个对象中的 list 成员变量
method3 与 method2 分析相同

②将 list 修改为局部变量那么就不会有上述问题了

class ThreadSafe {public final void method1(int loopNumber) {ArrayList<String> list = new ArrayList<>();for (int i = 0; i < loopNumber; i++) {method2(list);method3(list);}}private void method2(ArrayList<String> list) {list.add("1");}private void method3(ArrayList<String> list) {list.remove(0);}
}

分析:
list 是局部变量,每个线程调用时会创建其不同实例,没有共享
而 method2 的参数是从 method1 中传递过来的,与 method1 中引用同一个对象
method3 的参数分析与 method2 相同

4.Monitor

4.1 Java 对象头

(1)java的对象头由以下三部分组成:
①Mark Word
Mark Word记录了对象和锁有关的信息,当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关。
Mark Word在32位JVM中的长度是32bit,在64位JVM中长度是64bit。

在这里插入图片描述

②指向类的指针
该指针在32位JVM中的长度是32bit,在64位JVM中长度是64bit。
Java对象的类数据保存在方法区。
③数组长度(只有数组对象才有)
只有数组对象保存了这部分数据。该数据在32位和64位JVM中长度都是32bit。
(2)普通对象
在这里插入图片描述
(3)数组对象
在这里插入图片描述

4.2 Monitor概念

(1)Monitor被翻译为监视器或管程(由操作系统提供)
(2)每个Java对象都可以关联一个Monitor对象,如果使用synchronized给对象上锁(重量级)之后,该对象头的Mark Word中就会被设置指向Monitor对象的指针
(3)Monitor的结构如下:
在这里插入图片描述
①刚开始Monitor中Owner为null
②当Thread-2执行synchronized(obj)就会将Monitor的所有者Owner置为Thread-2,Monitor中只能有一个Owner
③在Thread-2上锁的过程中,如果Thread-3,Thread-4,Thread-5也来执行synchronized(obj),就会进入EntryList BLOCKED
④Thread-2执行完同步代码块的内容,然后唤醒EntryList中等待的线程来竞争锁,竞争的时候是非公平的
注意:
①synchronized必须是进入同一个对象的monitor才有上述的效果
②不加synchronized的对象不会关联监视器,不遵从以上规则

5.synchronized原理

5.1 轻量级锁

轻量级锁的使用场景:如果一个对象虽然有多线程访问,但多线程访问的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化。轻量级锁对使用者是透明的,即语法仍然是synchronized。例如:
在这里插入图片描述

  • 加锁
    (1)方法被调用时会产生一个栈帧,线程0执行到method1()的synchronized(obj)时会在线程的栈帧中创建锁记录(Lock Record)对象(该对象对我们是不可见的,是JVM层面的),每个线程的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word
    在这里插入图片描述

(2)让锁记录中的Object reference指向锁对象,尝试用cas把锁记录中的数据和锁对象中的Mark Word做一个交换,交换是为了表示加锁。
在这里插入图片描述

①如果cas替换成功,对象头中存储了锁记录地址状态00,表示由该线程给对象加锁,这时图示如下
在这里插入图片描述
②如果cas失败,有两种情况:
一种是其他线程已经持有了该Object的轻量级锁,这时表明有竞争,进入锁膨胀过程;
另一种是自己执行了synchronized锁重入,那么再添加一条Lock Record作为重入的计数
在这里插入图片描述

  • 解锁
    (1)当退出synchronized代码块(解锁时),如果有取值为null的锁记录,表示有重入,这时重置锁记录,重入计数减一
    在这里插入图片描述
    (2)当退出synchronized代码块(解锁时),锁记录的值不为null,这时使用cas将Mark Word的值恢复给对象头
    ①成功,则解锁成功
    ②失败,说明轻量级锁进行了锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程

5.2 锁膨胀

如果在尝试加轻量级锁的过程中,CAS操作无法成功,这时一种情况就是有其他线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁
在这里插入图片描述
(1)当Thread-1进行轻量级加锁时,Thread-0已经对该对象加了轻量级锁

在这里插入图片描述

(2)这时Thread-1加轻量级锁失败,进入锁膨胀流程。
①即为Object对象申请Monitor锁,让Object执行指向重量级锁地址。
②然后自己进入Monitor的EntryList BLOCKED
在这里插入图片描述
(3)当Thread-0退出同步代码块解锁时,使用cas将Mark Word的值恢复给对象头,失败。这时会进入重量级解锁流程,即按照Monitor地址找到Monitor对象,设置Owner为null,唤醒EntryList中BLOCKED线程

5.3 自旋优化

重量级锁竞争的时候,还可以使用自旋来进行优化,如果当前线程自旋成功,这时当前线程就可以避免阻塞。(自旋即让这个线程先不进入阻塞,而是进行几次循环,如果在循环的过程持锁线程已经退出了同步块释放了锁,就可以避免阻塞)
(1)自旋重试成功和失败的情况
①自旋重试成功
在这里插入图片描述
②自旋重试失败
在这里插入图片描述
(2)在Java6之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,是比较智能的。
(3)自旋会占用CPU时间,单核CPU自旋就是浪费,多核CPU自旋才能发挥优势。
(4)Java7之后不能控制是否开启自旋功能。

5.4 偏向锁

轻量级锁在没有竞争时(只有自己这个线程),每次重入仍然需要执行CAS操作。Java6中引入了偏向锁来做进一步优化:只有第一次使用CAS将线程ID设置到对象的Mark Word头,之后发现这个线程ID是自己就表示没有竞争,不用重新CAS,以后只要不发生竞争,这个对象就归该线程所有
例如:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.4.1 偏向状态

在这里插入图片描述
(1)一个对象创建时:
①如果开启了偏向锁(默认开启),那么对象创建后,markword值位0x05即最后3位为101,这时它的thread、epoch、age都为0
②偏向锁是默认延迟的,不会在程序启动时立即生效,如果想避免延迟,可以加VM参数
-XX:BiasedLockingStartupDelay=0来禁用延迟
③如果没有开启偏向锁,那么对象创建后,markword值为0x01即最后3位为001,这时它的hashcode、age都为0,第一次用到hashcode时才会赋值
(2)禁用偏向锁
添加VM参数 -XX:-UseBiasedLocking禁用偏向锁

5.4.2 撤销偏向锁

5.4.2.1 撤销-调用对象hashCode

调用对象的hashCode()方法,会禁用掉偏向锁。因为如果处于偏向锁的对象头只能存线程ID,存不下哈希码了
在这里插入图片描述

5.4.2.3 撤销-其他线程使用对象

当有其他线程使用偏向锁对象时,会将偏向锁升级为轻量级锁

5.4.2.4 撤销- 调用wait/notify

只有重量级锁才有wait/notify方法

5.4.3 批量重偏向

(1)如果对象虽然被多个线程访问,但没有竞争,这时偏向了线程T1的对象仍有机会重新偏向T2,重偏向会重置对象的Thread ID
(2)当撤销偏向随的阈值超过20次后,jvm会觉得是不是偏向错了,于是会在给这些对象加锁时重新偏向至加锁线程

5.4.4 批量撤销

当撤销偏向锁阈值超过40次后,jvm就会这样觉得,自己是不是偏向错了,根本就不该偏向。于是整个类的所有对象都会变为不可偏向的,新建的对象也是不可偏向的。

5.4.5 锁消除

锁消除即删除不必要的加锁操作。JVM在运行时,对一些“在代码上要求同步,但是被检测到不可能存在共享数据竞争情况”的锁进行消除。

5.4.6 锁粗化

假设一系列的连续操作都会对同一个对象反复加锁及解锁,甚至加锁操作是出现在循环体中的,即使没有出现线程竞争,频繁地进行互斥同步操作也会导致不必要的性能损耗。如果JVM检测到有一连串零碎的操作都是对同一对象的加锁,将会扩大加锁同步的范围(即锁粗化)到整个操作序列的外部。

6.wait/notify

6.1 wait/notify原理

在这里插入图片描述
(1)线程获取某个对象的Monitor锁,Owner线程发现条件不满足,调用wait方法,即可进入WaiSet变为WAITING状态
(2)BLOCKED和WAITING的线程都处于阻塞状态,不占用CPU时间片
(3)BLOCKED线程会在Owner线程释放锁时唤醒
(4)WAITING线程会在Owner线程调用notify或notifyAll时唤醒,但唤醒后并不意味着立刻获得锁,仍需进入EntryList重新竞争

6.2 API介绍

  • obj.wait() 让已经进入 object 监视器的线程到 waitSet 等待
  • obj.notify() 让object 上正在 waitSet 等待的线程中挑一个唤醒
  • obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒

它们都是线程之间进行协作的手段,都属于 Object 对象的方法。必须获得此对象的锁,才能调用这几个方法

final static Object obj = new Object();
public static void main(String[] args) {new Thread(() -> {synchronized (obj) { // 必须获得此对象的锁,才能调用API方法log.debug("执行....");try {obj.wait(); // 让线程在obj上一直等待下去} catch (InterruptedException e) {e.printStackTrace();}log.debug("其它代码....");}}).start();new Thread(() -> {synchronized (obj) {log.debug("执行....");try {obj.wait(); // 让线程在obj上一直等待下去} catch (InterruptedException e) {e.printStackTrace();}log.debug("其它代码....");}}).start();// 主线程两秒后执行sleep(2);log.debug("唤醒 obj 上其它线程");synchronized (obj) {obj.notify(); // 唤醒obj上一个线程// obj.notifyAll(); // 唤醒obj上所有等待线程}
}

6.3 wait、notify 的正确使用

(1)sleep(long n) 和 wait(long n) 的区别
①sleep是 Thread 方法,而 wait 是 Object 的方法
②sleep不需要强制和 synchronized 配合使用,但 wait 需要和 synchronized 一起用
③sleep在睡眠的同时,不会释放对象锁的,但 wait 在等待的时候会释放对象锁
④ 它们的状态都是TIMED_WAITING

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

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

相关文章

Pycharm安装,环境初次配置与运行第一个简单程序

一、Pycharm安装 1.在PyCharm官网中&#xff0c;找到社区版下载链接&#xff0c;下载Pycharm社区版&#xff0c;社区版免费 2.下载成功后&#xff0c;双击下载好的安装包&#xff0c;点击下一步后&#xff0c;点击“浏览”更改安装路径到C盘以外其他硬盘&#xff0c;点击“下…

数字化审计智慧

简析内部审计数字化转型的方法和路径 内部审计是一种独立的、客观的确认和咨询活动&#xff0c;包括鉴证、识别和分析问题以及提供管理建议和解决方案。狭义的数字化转型是指将企业经营管理和业务操作的各种行为、状态和结果用数字的形式来记录和存储&#xff0c;据此再对数据进…

ChatGPT预训练的奥秘:大规模数据、Transformer架构与自回归学习【文末送书-31】

文章目录 ChatGPT原理与架构ChatGPT的预训练ChatGPT的迁移学习ChatGPT的中间件编程 ChatGPT原理与架构&#xff1a;大模型的预训练、迁移和中间件编程【文末送书-31】 ChatGPT原理与架构 近年来&#xff0c;人工智能领域取得了巨大的进展&#xff0c;其中自然语言处理&#xf…

一款开源、免费、跨平台的Redis可视化管理工具

前言 经常有小伙伴在技术群里问&#xff1a;有什么好用的Redis可视化管理工具推荐的吗&#xff1f;, 今天大姚给大家分享一款我一直在用的开源、免费&#xff08;MIT License&#xff09;、跨平台的Redis可视化管理工具&#xff1a;Another Redis Desktop Manager。 Redis介绍…

【亲测有效】解决三月八号ChatGPT 发消息无响应!

背景 今天忽然发现 ChatGPT 无法发送消息&#xff0c;能查看历史对话&#xff0c;但是无法发送消息。 可能的原因 出现这个问题的各位&#xff0c;应该都是点击登录后顶部弹窗邀请 [加入多语言 alapha 测试] 了&#xff0c;并且语言选择了中文&#xff0c;抓包看到 ab.chatg…

【Python】成功解决ModuleNotFoundError: No module named ‘matplotlib‘

【Python】成功解决ModuleNotFoundError: No module named ‘matplotlib’ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448…

PyCharm如何添加python库

1.使用pip命令在国内源下载需要的库 下面使用清华源&#xff0c;在cmd中输入如下命令就可以了 pip install i https://pypi.tuna.tsinghua.edu.cn/simple 包名版本号2.如果出现报错信息&#xff0c;Cannot unpack file…这种情况&#xff0c;比如下面这种 ERROR: Cannot unpa…

数据结构奇妙旅程之二叉平衡树

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

图神经网络实战(4)——基于Node2Vec改进嵌入质量

图神经网络实战&#xff08;4&#xff09;——基于Node2Vec改进嵌入质量 0. 前言1. Node2Vec 架构1.2 定义邻居1.2 在随机游走中引入偏向性1.3 实现有偏随机游走 2. 实现 Node2Vec小结系列链接 0. 前言 Node2Vec 是一种基于 DeepWalk 的架构&#xff0c;DeepWalk 主要由随机游…

qt 格式化打印 日志 QMessagePattern 格式词法语法及设置

一、qt源码格式化日志 关键内部类 QMessagePattern qt为 格式化打印日志 提供了一个简易的 pattern(模式/格式) 词法解析的简易的内部类QMessagePattern,作用是获取和解析自定义的日志格式信息。 该类在qt的专门精心日志操作的源码文件Src\qtbase\src\corelib\global\qloggi…

[LeetCode][226]翻转二叉树

题目 226. 翻转二叉树 给你一棵二叉树的根节点 root&#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1] 示例 2&#xff1a; 输入&#xff1a;root [2,1,3] 输出&#x…

深度学习500问——Chapter02:机器学习基础(5)

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 2.14 贝叶斯分类器 2.14.1 图解极大似然估计 极大似然估计的原理&#xff0c;用一张图片来说明&#xff0c;如下图所示&#xff1a; 例&#xff1a;有两个外形完全相同的箱子&#xff0c;1号箱…

重学SpringBoot3-内容协商机制

重学SpringBoot3-内容协商机制 ContentNegotiationConfigurer接口配置内容协商URL参数Accept头使用Url扩展名 自定义内容协商格式步骤1: 注册自定义媒体类型步骤2: 实现HttpMessageConverter接口步骤3: 使用自定义HttpMessageConverter 注意点 在 Spring Boot 3 中&#xff0c;…

vue学习笔记21-组件传递数据_Props

组件与组件之间不是完全独立的&#xff0c;而是有交集的&#xff0c;那就是组件与组件之间是可以传递数据的 传递数据的解决方案就是props 父级&#xff1a; 在父级中引入子集 <template><h3>Parent</h3><Child/> </template><script> i…

GFP-GAN环境搭建推理测试

引子 近期&#xff0c;文生图&#xff0c;wav2lip很火&#xff0c;文生图&#xff0c;见识的太多&#xff0c;不多说了。wav2lip其通过语音驱动唇部动作并对视频质量进行修复&#xff0c;里面一般涉及到三个步骤&#xff0c;文本到语音转化&#xff0c;语音驱动唇部动作&#…

【C++初阶】第五站:C/C++内存管理 (匹配使用,干货到位)

前言&#xff1a; 本文知识点&#xff1a; 1. C/C内存分布2. C语言中动态内存管理方式3. C中动态内存管理4. operator new与operator delete函数 5. new和delete的实现原理 &#xff08;干货在此&#xff09; 6. 定位new表达式(placement-new)7. 常见面试题 目录 C/C内…

Java反射、枚举类和lambda表达式

前言&#xff1a; 本章我们就来了解Java中的反射和枚举类。枚举类和反射其实有些关系&#xff0c;接下来我们就来学习他们的使用。 反射&#xff1a; 反射的作用&#xff1a; 反射&#xff1a;反射允许对成员变量&#xff0c;成员方法和构造方法的信息进行编程访问。 Java中有…

CVE-2021-31440:eBPF verifier __reg_combine_64_into_32 边界更新错误

文章目录 前言漏洞分析构造 vuln reg 漏洞利用漏洞修复参考 前言 影响版本&#xff1a;Linux 5.7 ~ 5.11.20 8.8 编译选项&#xff1a;CONFIG_BPF_SYSCALL&#xff0c;config 所有带 BPF 字样的编译选项。General setup —> Choose SLAB allocator (SLUB (Unqueued Allocat…

从0到1手把手实现RPC|01 RpcProvider本地实现

RPC的简化版原理如下图&#xff08;核心是代理机制&#xff09;。 1.本地代理存根: Stub2.本地序列化反序列化3.网络通信4.远程序列化反序列化5.远程服务存根: Skeleton6.调用实际业务服务7.原路返回服务结果8.返回给本地调用方 注意处理异常。 RpcProvider的本地实现 1、工…

xss.haozi.me靶机 通关

0x00 没有任何过滤可以直接注入<img srcx οnerrοralert(1)> 0x01 使用了testarea标签将我们的输入内容以普通的字符串进行展示 但是我们可以将标签进行闭合 </textarea><img srcx οnerrοralert(1)> 0x02 我们依然可以先闭合之后添加属性a" οncl…