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

news2024/9/30 18:33:04

文章目录

  • 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.coloradmin.cn/o/1505766.html

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

相关文章

三阶导数在生活中应用

“鲍威尔还表示&#xff0c;美联储将在某个时候放慢&#xff08;利率&#xff09;加息步伐” 这是参考三阶导数&#xff08;贷款义务基础&#xff0c;利率一阶导数&#xff0c;利率变化二阶导数&#xff0c;利率变化速度三阶导数&#xff09;。 是否还有其他有趣的例子&#…

算法详解——leetcode150(逆波兰表达式)

欢迎来看博主的算法讲解 博主ID&#xff1a;代码小豪 文章目录 逆波兰表达式逆波兰表达式的作用代码将中缀表达式转换成后缀表达式文末代码 逆波兰表达式 先来看看leetcode当中的原题 大多数人初见逆波兰表达式的时候大都一脸懵逼&#xff0c;因为与平时常见的表达式不同&am…

华为配置DHCP Snooping防止DHCP Server仿冒者攻击示例

配置DHCP Snooping防止DHCP Server仿冒者攻击示例 组网图形 图1 配置DHCP Snooping防止DHCP Server仿冒者攻击组网图 DHCP Snooping简介配置注意事项组网需求配置思路操作步骤配置文件 DHCP Snooping简介 在一次DHCP客户端动态获取IP地址的过程中&#xff0c;DHCP Snoopi…

基于Vue的娱讯移动端APP前端设计与实现

目 录 摘 要 Abstract 引 言 1绪论 1.1课题背景及目的 1.1.1移动端APP发展简介 3 1.1.2移动端APP的优势 3 1.2前端开发相关技术 1.2.1前端开发工具介绍 3 1.2.2 前端开发相关技术介绍 4 1.3本章小结 2系统分析 2.1功能需求分析 2.2系统工作流程 2.3本章小结 3系统设…

Linux 之七:Linux 防火墙 和进程管理

防火墙 查看防火墙 查看 Centos7 的防火墙的状态 sudo systemctl status firewalld。 查看后&#xff0c;看到active(running)就意味着防火墙打开了。 关闭防火墙&#xff0c;命令为&#xff1a; sudo systemctl stop firewalld。 关闭后查看是否关闭成功&#xff0c;如果…

python的scripts文件夹作用

Windows系统&#xff1a; Scripts文件夹通常位于Python的安装目录下&#xff0c;如C:\Python\Scripts。该文件夹内包含了各种有用的工具&#xff0c;例如pip、virtualenv等&#xff0c;这些工具有助于管理和配置Python环境和依赖包。 Linux系统&#xff1a; 在Linux系统中&…

【大厂AI课学习笔记NO.69】使用开源管理仓库

了解了开源框架&#xff0c;开源项目&#xff0c;今天来学习开源管理仓库。 我们先说Git&#xff0c;开源的版本管理分布式系统。 GitHub&#xff0c;则是世界上最大的代码托管平台&#xff0c;面向开源和私有项目托管。 有的人总是分不清这两个&#xff0c;其实一个是版本管…

凌鲨微应用架构

微应用是静态网页加上凌鲨提供的扩展能力而形成的一种应用&#xff0c;主要特点是开发便捷&#xff0c;安全。 微应用架构 组件说明 名称 说明 微应用 webview窗口&#xff0c;显示web服务器上的页面 接口过滤器 根据权限配置,屏蔽非授权接口访问 接口提供者 tauri注入…

循序渐进丨MogDB 数据库特性之动态数据脱敏机制

数据脱敏是行之有效的数据库隐私保护方案之一&#xff0c;可以在一定程度上限制非授权用户对隐私数据的窥探。动态数据脱敏机制是一种通过定制化脱敏策略来实现对隐私数据保护的技术&#xff0c;可以在保留原始数据的前提下有效地解决非授权用户对敏感信息访问的问题。当管理员…

C#,蛇梯问题(Snake and Ladder Problem)的算法与源代码

1 蛇梯问题 Snake and Ladder Problem 给定一个蛇梯板&#xff0c;找出从源单元格或第一个单元格到达目标单元格或最后一个单元格所需的最小掷骰次数。基本上&#xff0c;玩家可以完全控制掷骰子的结果&#xff0c;并希望找出到达最后一个单元格所需的最小掷骰次数。 如果玩…

基于鳑鲏鱼优化算法(Bitterling Fish Optimization,BFO)的无人机三维路径规划

一、无人机路径规划模型介绍 无人机三维路径规划是指在三维空间中为无人机规划一条合理的飞行路径&#xff0c;使其能够安全、高效地完成任务。路径规划是无人机自主飞行的关键技术之一&#xff0c;它可以通过算法和模型来确定无人机的航迹&#xff0c;以避开障碍物、优化飞行…

gradle下载太慢者超时!国内镜像可以直接下载

# 解决Gradle下载过慢问题的有效方式&#xff1a;使用国内镜像站点 在开发过程中&#xff0c;我们经常会遇到Gradle下载速度缓慢或超时的问题。作为一个强大而流行的构建工具&#xff0c;Gradle是许多项目中必不可少的一部分。然而&#xff0c;由于官方下载地址可能受网络限制…

Windows10/11配置WSL(Ubuntu)环境

文章目录 WSL介绍WSL部署扩展&#xff1a;辅助工具Windosw Terminal安装下载 WSL介绍 传统方式获取Linux操作系统&#xff0c;是安装完整的虚拟机及镜像环境&#xff0c;例如虚拟机VMware 而使用WSL,可以以非常轻量化的方式&#xff0c;得到Linux系统环境 它无需单独虚拟一套硬…

React 19 Cheat Sheet

React 19让构建网站和应用程序变得更容易&#xff0c;更好。有了很酷的新东西&#xff0c;比如React编译器、Actions API和更好的Hooks&#xff0c;编写代码变得更快&#xff0c;管理应用程序的数据变得更简单 React 19让构建网站和应用程序变得更容易&#xff0c;更好。有了很…

vue组件之间通信方式汇总

方式1&#xff1a;props和$emit props和$emit仅仅限制在父子组件中使用 1.props&#xff1a;父组件向子组件传递数据 1.1 代码展示 <template><div><!-- 这是父组件 --><div>父组件中的基本数据类型age的值是:{{this.age}}</div><div>…

C++的一些基础语法

前言&#xff1a; 本篇将结束c的一些基础的语法&#xff0c;方便在以后的博客中出现&#xff0c;后续的一些语法将在涉及到其它的内容需要用到的时候具体展开介绍&#xff1b;其次&#xff0c;我们需要知道c是建立在c的基础上的&#xff0c;所以c的大部分语法都能用在c上。 1.…

【Docker】容器的概念

容器技术&#xff1a;容器技术是基于虚拟化技术的&#xff0c;它使应用程序从一个计算机环境快速可靠地转移到另一个计算机环境中&#xff0c;可以说是一个新型地虚拟化技术。 一、docker容器 Docker:是一个开源地容器引擎Docker 是一种轻量级的容器化技术&#xff0c;其主要原…

雷赛控制卡获取轴当前位置的值不正确问题处理

现像 从雷赛控制卡中获取当前轴位置值时发现轴在向零点的右边走时显示的值是负数。正常来就一般是要反馈正数的。一般轴零点右边是正方向&#xff0c;限位是正限位&#xff0c;反馈的位置也应该是正数。 如果雷赛软件中的【单轴参数】中的基本设置中的【脉冲模式】设置的是对的…

【神经网络与深度学习】LSTM(Long Short-Term Memory)神经网络模型

概述 LSTM&#xff08;Long Short-Term Memory&#xff09;是一种特殊的循环神经网络&#xff08;RNN&#xff09;结构&#xff0c;通常被用于处理和学习时间序列数据。因此&#xff0c;LSTM属于深度学习领域中的一种神经网络模型。 在深度学习中&#xff0c;LSTM被广泛应用于…

【轮式平衡机器人】——TMS320F28069片内外设之eCAP

引入 TMS320F28069的eCAP&#xff08;增强型捕获模块&#xff09;是一个强大的外设&#xff0c;用于精确测量和捕获输入信号的事件和时间戳。 在电机控制、传感器数据采集和信号处理等应用中&#xff0c;eCAP模块可以用于测量霍尔传感器、编码器或其他数字输入信号的周期、频…