Java内存模型(JMM)

Volatile关键字

如何保证变量的可见性

在Java中,Volatile关键字可以保证变量的可见性,如果我们将变量声明为volatile,这就指示JVM,这个变量是共享且不稳定的,**每次使用它都到主存中进行读取(禁止读取本地内存的共享变量副本)**

volatile关键字能保证数据的可见性,但是不能保证数据的原子性。sychronized关键字两者都能保证。

如何禁止指令重排序

在Java中,volatile关键字除了保证变量的可见性,还有一个重要的作用就是防止JVM指令重排序。如果,我们将变量声明为volatile,在对这个变量进行读写操作的时候,会通过插入特定的内存屏障的方式来禁止指令重排序,下面结合单例模式进行讲解。

双重校验锁实现对象单例(线程安全)

public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() {}public  static Singleton getUniqueInstance() {//先判断对象是否已经实例过,没有实例化过才进入加锁代码if (uniqueInstance == null) {//类对象加锁synchronized (Singleton.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton();}}}return uniqueInstance;}
}

uniqueInstance 采用volatile关键字修饰也是很有必要的,uniqueInstance = new Singleton();这段代码其实是分为三步执行的:

  1. 为uniqueInstance分配内存空间
  2. 初始化uniqueInstance
  3. 将uniqueInstance指向分配的内存空间。

但是由于JVM具有指令重排的特性,执行顺序有可能变成了1->3->2,指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致线程获得一个还没有初始化的实例。例如,线程T1执行了1和3.此时T2调用getUniqueInstance()发现uniqueInstance不为空,因此返回uniqueInstance,但此时的uniqueInstance尚未被初始化。

volatile可以保证原子性吗

volatile关键字可以保证变量的可见性,但不能保证对变量的操作是原子性的。

public class VolatileAtomicityDemo {public volatile static int inc = 0;public void increase() {inc++;}public static void main(String[] args) throws InterruptedException {ExecutorService threadPool = Executors.newFixedThreadPool(5);VolatileAtomicityDemo volatileAtomicityDemo = new VolatileAtomicityDemo();for (int i = 0; i < 5; i++) {threadPool.execute(() -> {for (int j = 0; j < 500; j++) {volatileAtomicityDemo.increase();}});}// 等待1.5秒,保证上面程序执行完成Thread.sleep(1500);System.out.println(inc);threadPool.shutdown();}
}

在这段代码中,5个线程分别进行了500次操作,那么最终inc的值应该输出2500.但是实际运行会发现每次输出的 结果都是小于2500的。因为volatile关键字虽然可以保证内存的可见性,但是无法保证内存的原子性。代码中的inc++操作实际是一个复合操作,包括三步:

  • 读取inc的值
  • 对inc加1
  • 将inc的值写回内存

volatile无法保证这三个操作的原子性,有可能出现下面的情况:

  1. 线程1对inc进行读取操作之后,还未对其进行修改,线程2又读取了inc的值并对其进行修改,再将inc的值写回内存。
  2. 线程2操作完毕后,线程1对inc的值进行修改,在将inc的值写回内存。

这就导致了两个线程分别对inc进行了一次自增操作后,inc的值实际上只增加了1.

乐观锁和悲观锁

悲观锁

什么是悲观锁

悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁。这样其他线程想要获取到这个资源就会阻塞直到锁被上一个持有者释放,也就是说,共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程。

Sychrnoized和ReentrantLock等独占锁都属于悲观锁的思想。

public void performSynchronisedTask() {synchronized (this) {// 需要同步的操作}
}
private Lock lock = new ReentrantLock();
lock.lock();
try {// 需要同步的操作
} finally {lock.unlock();
}

高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增加系统的性能开销,并且,悲观锁可能会存在死锁问题,影响代码的正常运行。

Sychronized关键字

Sychronized是什么?有什么用?

Sychronized是Java中的一个关键字,主要解决的是多个线程之间访问资源的同步性,可以保证被他修饰的方法或者代码块在任意时刻只能有一个线程执行。
在Java早期版本中,Sychronized属于重量级锁,效率低下。这是因为监视器锁是依赖于底层的操作系统的Mutex Lock来实现的,Java的线程是映射到操作系统的原生线程上的,如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对较长的时间成本。

在Java6之后,sychronized引入了大量的优化如自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少操作的开销,这些优化让sychronized性能提升了很多。

偏向锁:由于偏向锁增加了JVM的复杂性,同时也没有为所有应用都带来性能提升,在JDK15中,偏向锁被默认关闭(可以使用-XX:+UseBiasedLocking 启用偏向锁),JDK18,偏向锁已经被彻底废弃。

如何使用Sychronized

Sychronized关键字使用方式有下面几种:

  1. 修饰实例方法
  2. 修饰静态方法
  3. 修饰代码块

1. 修饰实例方法(锁当前对象实例)
给当前对象实例加锁,进入同步代码前要获得当前对象实例的锁

synchronized void method() {//业务代码
}

2. 修饰静态方法(锁当前类)

给当前类加锁,会作用于类的所有对象实例,进入同步代码前要获得当前类的锁 。

这是因为静态成员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,被类的所有实例共享。

synchronized void method() {//业务代码
}

静态synchronized方法和非静态synchronized方法之间的调用不互斥,即如果一个线程A调用一个实例对象的非静态synchronized方法,而线程B调用这个实例对象所属类的静态synchronized方法,是允许的,不会发生互斥现象,因为访问静态synchronized方法占用的锁是当前类的锁,访问非静态synchronized占用的锁是当前实例对象锁

3. 修饰代码块(锁指定对象/类)

  • synchronized(object) 表示进入同步代码库前要获得给定对象的锁
  • synchronized(类.class) 表示进入同步代码前要获得给定class的锁
synchronized (this) {// this 锁的是当前对象,并非class//业务代码
}

总结:

  • synchronized关键字加到static静态方法和synchronized(class)代码块上都是给Class类上锁
  • synchronized关键字加到实例方法上是给对象实例上锁。
  • 尽量不要使用synchronized(String a) 因为JVM中,字符串常量池具有缓存功能。

构造方法可以用sychronized修饰吗

构造方法不能使用sychronized修饰,不过,可以在构造方法内部使用sychronized代码块。

另外,构造方法本身是线程安全的,但如果在构造方法中涉及到共享资源的操作,就需要采取适当的同步措施来保证整个构造过程的线程安全。

sychronized底层原理

synchronized 同步语句块的情况
通过使用javap -c -s -v -l SynchronizedDemo.class,得到如下的编译文件

public class SynchronizedDemo {public void method() {synchronized (this) {System.out.println("synchronized 代码块");}}
}

在这里插入图片描述

从上面可以看出:synchronized同步语句块的实现使用的是monitorenter和monitorexit指令,其中monitorenter指令指向同步代码块的开始位置,monitorexit指令则指明同步代码块结束位置。

上面的字节码中包含一个monitorenter和两个monitorexit指令,这是为了保证锁在同步代码块代码正常执行以及异常的两种情况下都能正确释放。

当执行monitorenter指令时,线程试图获取锁也就是获取 对象监视器monitor的持有权。

在Java虚拟机中,monitor是基于C++实现的,由ObjectMonitor实现的,每个对象中都内置了ObjectMonitor。

另外,wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException异常。

在执行monitorenter时,会尝试获取对象的锁,如果锁的计数器为0则表示锁可以被获取,获取锁后将计数器加1.

对象锁的持有者线程可以执行monitorexit指令来释放锁,在执行monitorexit指令后,将锁的计数器减1.表示锁被释放,其他线程可以尝试获取锁。

如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另一个线程释放为止。

synchronized 修饰方法的情况

public class SynchronizedDemo2 {public synchronized void method() {System.out.println("synchronized 方法");}
}

在这里插入图片描述
synchronized修饰的方法并没有monitorenter和monitorexit指令,取而代之的是ACC_SYNCHRONIZED标识,这个标识指明该方法是一个同步方法。JVM通过ACC_SYNCHRONIZED访问标志来辨别一个方法是否为同步方法,从而执行相应的同步调用。

如果是实例方法,JVM会尝试获取实例对象的锁;如果是静态方法,JVM会尝试获取当前class的锁。

synchronized底层实现原理总结:

  1. synchronized同步语句块的实现使用的是monitorenter和monitorexit指令,其中monitorenter指明同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置。
  2. synchronized修饰的方法并没有monitorenter和monitorexit指令,取而代之的是ACC_SYNCHRONIZED标识,这个标识指明该方法是一个同步方法。

但是两者的本质都是获取对象监视器monitor

synchronized和volatile的区别

  • volatile关键字是线程同步的轻量级实现,所以性能好,但是volatile关键字只能用于变量,而synchronized关键字可以修饰方法以及代码块。
  • volatile保证的是数据的可见性,但不能保证数据的原子性,而synchronized都能保证。
  • volatile关键字解决的是多个线程之间共享变量的可见性,而synchronized保证的是多个线程之间访问资源的互斥以及同步。

ReentrantLock

ReentrantLock是什么

ReentrantLock实现了Lock接口,是一个可重入且独占式的锁,和synchronized关键字类似,不过ReentrantLock更灵活、强大、增加了轮询、超时、中断、公平锁和非公平锁等高级功能。

ReentrantLock里面有一个内部类Sync,Sync继承AQS(AbstractQueuedSynchronizer),添加锁和释放锁的大部分操作都是在Sync中实现的。Sync有公平锁FairSync和非公平锁 NonFairSync 两个类。

在这里插入图片描述
ReentrantLock 默认使用非公平锁,也可以通过构造器来显式的指定使用公平锁。

// 传入一个 boolean 值,true 时为公平锁,false 时为非公平锁
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}

公平锁和非公平锁有什么区别

  • 公平锁:锁被释放后,先申请的线程先得到锁,性能较差,因为公平锁为了保证时间上的绝对顺序,上下文切换更频繁。
  • 非公平锁:锁被释放之后,后申请的线程可能会先获取到锁,是随机或者按照其他优先级排序的,性能更好,但可能会出现饥饿问题。

synchronized 和 ReentrantLock 有什么区别

 1.  两者都是可重入锁。2. synchronized依赖于JVM,而ReentrantLock依赖于API3. ReentrantLock功能更强大,提供了很多高级特性。

1.两者都是可重入锁

可重入锁也叫递归锁,指的是线程可以再次获取自己内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其想要再次获取这个对象锁的时候还是可以获取的。如果不可重入锁的话,就会造成死锁。

JDK 提供的所有现成的 Lock 实现类,包括 synchronized 关键字锁都是可重入的。

public class SynchronizedDemo {public synchronized void method1() {System.out.println("方法1");method2();}public synchronized void method2() {System.out.println("方法2");}
}

由于synchronized是可重入的,同一个线程在调用method1()时可以直接获取当前对象的锁,执行method2()时可以再次获取这个对象的锁,不会产生死锁问题。假如synchronized是不可重入的,由于该对象的锁已被当前线程持有且无法释放,这就导致线程在执行method2()时获取锁失败,会出现死锁问题。

2. synchronized依赖于JVM,而ReentrantLock依赖于API

synchronized同步语句块的实现使用的是monitorenter和monitorexit指令,其中monitorenter指明同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置。synchronized修饰的方法并没有monitorenter和monitorexit指令,取而代之的是ACC_SYNCHRONIZED标识,这个标识指明该方法是一个同步方法;ReentrantLock是JDK层面实现的,需要lock和unlock方法进行加锁和解锁。

3. ReentrantLock功能更强大,提供了很多高级特性

  • 等待可中断:ReentrantLock提供了一种能够中断等待锁的机制,通过lock.lockInterruptibly()来实现这个机制。即正在等待的线程可以选择放弃等待,改为处理其他事情。
  • 可实现公平锁:ReentrantLock默认情况是非公平的,可以通过 ReentrantLock类的ReentrantLock(boolean fair)构造方法来指定是否是公平的。
  • 可实现选择性通知:synchronized关键字与wait和notify结合可以实现等待/通知机制。ReentrantLock借助于Condition接口和newCondition方法也可以实现。

Condition接口

Condition是JDK1.5之后才有的,具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。在使用notify/notifyAll方法进行通知时,被通知的线程是由JVM选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知”。而synchronized关键字相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在他一个身上,如果执行notifyAll时就会通知所有处于等待状态的线程,这样会造成很大的效率问题。而Condition实例的signalAll()方法,只会唤醒注册在该Condition实例中的所有等待线程。

Condition实例

package example;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockConditionExample {public static final ReentrantLock reentrantLock = new ReentrantLock();public static final Condition condition = reentrantLock.newCondition();public static boolean metCondition = false;public void method1() {reentrantLock.lock();if (!metCondition) {try {System.out.println("线程等待");//await操作释放线程当前持有的锁condition.await();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程被唤醒。。。");reentrantLock.unlock();}public void method2() {reentrantLock.lock();metCondition = true;System.out.println("唤醒线程。。。");//signal之后唤醒等待在condition上的所有线程,被唤醒的线程会尝试重新获取锁,获取到所以后继续执行condition.signalAll();reentrantLock.unlock();}
}class Test {public static void main(String[] args) throws InterruptedException {ReentrantLockConditionExample example = new ReentrantLockConditionExample();Thread waitThread1 = new Thread(() -> {example.method1();});Thread waitThread2 = new Thread(() -> {example.method1();});Thread signalThread = new Thread(() -> {example.method2();});waitThread1.start();waitThread2.start();Thread.sleep(5000);signalThread.start();}
}

可中断锁和不可中断锁有什么区别

  • 可中断锁:获取锁的过程中可以被中断,不需要一直等到获取锁之后才能进行逻辑处理。ReentrantLock就是可中断锁。
  • 不可中断锁:一旦线程申请了锁,就只能等待拿到锁之后才能进行其他逻辑处理,synchronized就是属于不可中断锁。
乐观锁

什么是乐观锁

乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停的执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源是否被其他线程修改了具体方法可以使用版本号机制或者CAS算法)。

总结:高并发的场景下,乐观锁相比悲观锁来说,不存在锁竞争造成线程阻塞,也不会有死锁问题。在性能上往往更胜一筹。但是,如果冲突频繁发生(写占比非常多的情况下),会频繁失败和重试,这样同样非常影响性能,导致CPU飙升。

理论上来说:

  • 悲观锁通常多用于写比较多的情况,这样可以避免频繁失败和重试影响性能,悲观锁的开销是固定的。
  • 乐观锁通常多用于写比较少的情况,这样可以避免频繁加锁影响性能。

如何实现乐观锁

乐观锁一般使用版本号机制或者CAS算法实现。

版本号机制
一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数。当数据被修改时,version值会加1. 当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中version值相等时才更新,否则放弃此次操作。

CAS算法

CAS全称是Compare And Swap(比较和交换),用于实现乐观锁,CAS的思想就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新。

CAS是一个原子操作,底层依赖于一条CPU的原子指令。

原子操作:即最小不可拆分的操作,也就是说操作一旦开始,就不能被打断,直到操作完成。

CAS涉及到三个操作数

  • V 要更新的变量值
  • E 预期值
  • N 拟写入的新值

当且仅当V的值等于E时,CAS通过原子方式用新值N来更新V的值。如果不等,说明已经有其他线程更新了V,则当前线程放弃更新。

举例说明:

线程A要修改变量 i 的值为6,i原值为1(假设不存在ABA问题,V =1,E=1,N=6)

  1. i与1进行比较,如果相等,则说明没被其他线程修改,可以被设置为6.
  2. i与1进行比较,如果不相等,则说明没被其他线程修改,当前线程放弃更新,CAS操作失败。

当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。

CAS算法存在的问题

1. ABA问题

如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到仍然是A值,那我们就能说明它没有被其他线程修改过了吗?答案是不能,因为在这段时间他的值可能被修改为其他值,然后又改回了A,那CAS操作就会误认他从来没有被修改过,这就是CAS的“ABA”问题。

ABA问题的解决思路是在前面追加上版本号或者时间戳,JDK1.5以后的AtomicStampedReference类就是用来解决ABA问题的,其中的CompareAndSet()方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。

2. 循环时间开销大

CAS经常会用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功,如果长时间不成功,会给CPU带来非常大的执行开销。
如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用:

  1. 可以延迟流水线执行指令,使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。
  2. 可以避免在退出循环的时候因为内存顺序冲突而引起CPU流水线被清空,从而提高CPU的执行效率。

3. 只能保证一个共享变量的原子操作

CAS只对单个共享变量有效,当操作涉及多个共享变量时CAS无效,但是从JDK1.5开始,提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作,所以我们可以使用锁或者AtomicReference类把多个共享变量合并成一个共享变量来操作。

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

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

相关文章

二分查找算法介绍(边界值、循环条件、值的变化、二分查找的原理、异常处理)

一、二分查找法原理介绍 二分查找是经典的查找算法之一&#xff0c;其原理也非常简单。 对于已排序的数组&#xff08;假设是整型&#xff0c;如果非整型&#xff0c;如果有排序和大小比较的定义&#xff0c;也可以使用二分查找&#xff09;&#xff0c;我们每次判断中间值与目…

基于JSP的母婴用品网站系统

你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有需求可以文末加我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSP技术 工具&#xff1a;IDEA/Eclipse、Navicat、Maven 系统展示 首页 管理员功能界面 用户功能界面 前台首页功能界面 …

JUC从实战到源码:悲观锁和乐观锁真正了解了吗

【JUC】- 多线程与锁的知识 &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &#x1f31d;分享学习心得&#xff0c;欢迎指…

游戏找不到d3dcompiler43.dll怎么办,分享5种有效的解决方法

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是找不到某个文件。其中&#xff0c;找不到d3dcompiler43.dll是一个常见的问题。这个问题通常出现在运行某些游戏或应用程序时&#xff0c;由于缺少了d3dcompiler43.dll文件&#xff0c;导致程…

mysql的锁(全局锁)

文章目录 mysql按照锁的粒度分类全局锁概念&#xff1a;全局锁使用场景&#xff1a;全局锁备份案例&#xff1a; mysql按照锁的粒度分类 全局锁 概念&#xff1a; 全局锁就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法&#xff0c;命令是: Flush tables with…

#招聘数据分析#2024年5月前程无忧招聘北上广深成渝对比情况

#招聘数据分析#2024年5月前程无忧招聘北上广深成渝对比情况 0、根据前程无忧不完全样本统计&#xff0c;北上广深成都重庆平均月工资从高到低依次为 北京15037元、上海14230元、深圳13230元、广州11125元、成都10614元、重庆10388。 1、成都招聘样本数全量36301个&#xff0c…

搭建gateway网关

1.创建springBoot项目 可以将Server URL换成start.aliyun.com 2.配置路由与跨域处理 路由&#xff1a; server:port: 10010 # 网关端口 spring:application:name: gateway # 服务名称cloud:nacos:server-addr: localhost:8848 # nacos地址gateway:routes: # 网关路由配置- i…

JavaScript解构赋值

一、数组解构 以上要么不好记忆&#xff0c;要么书写麻烦&#xff0c;此时可以使用解构赋值的方法让代码更简洁。 数组解构是将数组的单元值快速批量赋值给一系列变量的简洁语法。 基本语法&#xff1a; 1、赋值运算符左侧的[]用于批量声明变量&#xff0c;右侧数组的单元值将…

MyBatis的各种查询功能

1、查询&#xff1a; 查询的标签select必须设置属性resultType或resultMap&#xff0c;用于设置实体类和数据库表的映射关系 resultType&#xff1a;自动映射&#xff0c;用于属性名和表中字段名一致的情况 resultMap&#xff1a;自定义映射&#xff0c;用于一对多或多对一或…

网络协议二

一、套接字Socket 基于 TCP UDP 协议的 Socket 编程&#xff0c;在讲 TCP 和 UDP 协议的时候&#xff0c;我们分客户端和服务端&#xff0c;在写程序的时候&#xff0c;我们也同样这样分。 在网络层&#xff0c;Socket 函数需要指定到底是 IPv4 还是 IPv6&#xff0c;分别对应设…

部署专属网页版ChatGPT-Next-Web

背景 工作学习中经常使用chat-gpt, 需求是多端使用gpt问答&#xff0c;因此搭建一个网页版本方便多个平台使用。最后选择了 ChatGPT-Next-Web 部署说明 一键部署自己的web页面&#xff0c;因为是使用免费的vercel托管的&#xff0c;vercel节点在全球都有&#xff0c;理论上突…

HSC Mailinspector loader.php 任意文件读取漏洞复现(CVE-2024-34470)

0x01 产品简介 HSC Mailinspector是一款远程电子邮件检查工具&#xff0c;支持POP3/IMAP4协议。它允许用户远程扫描最新邮件&#xff0c;并进行浏览、垃圾邮件排除、编辑、删除等操作&#xff0c;无需实际登录邮箱。 0x02 漏洞概述 由于HSC Mailinspector /public/loader.ph…

linux,lseek,append用法

打开写的.c文件 内容为 代码 <sys/stat.h> #include <fcntl.h> #include<stdio.h> #include<unistd.h> #include<string.h>//off_t lseek(int fd, off_t offset, int whence); //int open(const char *pathname, int flags); //int open(const …

【c++入门】函数重载,引用,内联函数,auto

函数重载 函数重载概念 什么是函数重载&#xff1f; 函数重载&#xff1a;是函数的一种特殊情况&#xff0c;C允许在同一作用域中声明几个功能类似的同名函数&#xff0c;这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同&#xff0c;常用来处理实现功能类似数据类…

基于鲲鹏服务器搭建简单的开源论坛系统(LAMP)实践分享

LAMPLinux apache mysql( mariadb) PHP 结合利用华为云弹性负载均衡ELB弹性伸缩AS服务 优点&#xff1a; 将访问流量自动分发到多台云服务器&#xff0c;扩展应用系统对外的服务能力&#xff0c;实现更高水平的应用容错&#xff1b; 根据不同的业务、访问需求和预设策略&…

Java编程常见问题汇总一

系列文章目录 文章目录 系列文章目录前言一、字符串连接误用二、错误的使用StringBuffer三、测试字符串相等性四、数字转换成字符串五、利用不可变对象(Immutable) 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分…

mac电脑用谷歌浏览器对安卓手机H5页面进行inspect

1、mac上在谷歌浏览器上输入 chrome://inspect 并打开该页面。 2、连接安卓手机到Mac电脑&#xff1a;使用USB数据线将安卓手机连接到Mac电脑。 3、手机上打开要的h5页面 Webview下面选择要的页面&#xff0c;点击inspect&#xff0c;就能像谷歌浏览器页面打开下面的页面&#…

大模型时代的具身智能系列专题(七)

北大王鹤团队 王鹤&#xff0c;北京大学前沿计算研究中心助理教授&#xff0c;本科毕业于清华大学&#xff0c;博士毕业于斯坦福大学&#xff0c;师从美国三院院士Leonidas. J Guibas教授。他创立并领导了具身感知与交互实验室(EPIC Lab)&#xff0c;实验室立足三维视觉感知与…

矩阵连乘问题

#include<iostream> using namespace std; #define N 7 void MatrixChain(int p[N],int n,int m[N][N],int s[N][N]) {for(int i1;i<n;i)m[i][i]0;for(int r2;r<n;r)//有多少个相乘(规模){for(int i1;i<n-r1;i){int jir-1;m[i][j]m[i][i]m[i1][j]p[i]*p[i1]*p[j…

【AREngine BUG 解决方法】无法获取有效的相机图像尺寸

近期拿了一台 华为mate20 Pro的手机&#xff0c;在运行AR示例的过程中出现了黑屏。 问题排查 SDK版本&#xff1a;com.huawei.hms:arenginesdk:3.7.0.3 定位 经排查&#xff0c;发现(ARCamera对象的相机内参) getImageDimensions()返回的图像尺寸的width和height都为0。 这…