JavaEE 初阶篇-深入了解 CAS 机制与12种锁的特征(如乐观锁和悲观锁、轻量级锁与重量级锁、自旋锁与挂起等待锁、可重入锁与不可重入锁等等)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍

文章目录

        1.0 乐观锁与悲观锁概述

        1.1 悲观锁(Pessimistic Locking)

        1.2 乐观锁(Optimistic Locking)

        1.3 区别与适用场景

        2.0 轻量级锁与重量级锁概述

        2.1 真正加锁的底层逻辑顺序

        2.2 轻量级锁

        2.3 重量级锁

        2.4 区别于适用场景

        2.5 轻量级锁与乐观锁的区别、重量级锁与悲观锁的区别

        3.0 自旋锁与挂起等待锁概述

        3.1 自旋锁(Spin Lock)

        3.2 挂起等待锁(Suspend-Resume Lock)

        3.3 自旋锁是一种典型的轻量级锁的实现方式

        4.0 公平锁与非公平锁概述

        4.1 公平锁(Fair Lock)

        4.2 非公平锁(Unfair Lock)

        5.0 可重入锁与不可重入锁概述

        6.0 读写锁与互斥锁概述

        6.1 读写锁(Read-Write Lock)

        6.2 互斥锁(Mutex Lock)

        7.0 CAS 概述

        7.1 CAS 的实际应用

        7.1.1 CAS 的实际应用 - 实现原子类

        7.1.2 CAS 的实际应用 - 自旋锁


        1.0 乐观锁与悲观锁概述

        乐观锁和悲观锁是两种并发控制的策略,用于处理多线程环境下的数据访问和更新。它们的主要区别在于对并发情况的预期和处理方式。

        synchronized 是乐观锁也悲观锁。

        1.1 悲观锁(Pessimistic Locking)

        悲观锁的基本思想是在操作数据之前先获取锁,假定会并发访问,因此在整个操作过程中都持有锁,以防其他线程对数据进行修改。悲观锁通常会导致其他线程在访问数据时被阻塞,以确保数据的一致性。

        简单来说,在多线程中对共享变量进行操作时,会认为其他线程都会对这个共享变量进行操作,因此,从悲观锁的角度来说,先加锁成功后,才能对共享变量进行操作;否则,只能阻塞等待锁释放。从而可以确保线程安全。

        常见的悲观锁:synchronized 关键字、ReentrantLock 等锁机制。

        1.2 乐观锁(Optimistic Locking)

        乐观锁的基本思想是假设在数据操作过程中不会发生并发冲突,因此不会立即加锁,而是在更新数据时检查是否有其他线程已经对数据进行修改了。如果没有发现数据被修改,那么继续操作;如果发现数据已经被修改了,会进行回滚或者重试。

        简单来说,在多线程中对共享变量进行操作时,一开始不会认为有其他线程会对该共享变量进行操作,认为当前线程可以安心的对该变量进行操作。执行到后面,如果发现已经有其他线程对该共享变量进行了操作了,那么当前的线程执行回滚或者重试。

        1.3 区别与适用场景

        synchronized 一开始是使用乐观锁策略,当发现锁竞争比较频繁的时候,就会自动切换悲观锁策略。

        悲观锁:真正加上了锁处理,适用于并发写入较多、冲突概率较高的场景,适合长事务处理。

        乐观锁:没有真正的加锁处理,适用于并发读取较多,冲突概率较低的场景,适合短事务处理。

        2.0 轻量级锁与重量级锁概述

        轻量级锁和重量级锁是 Java 中用于实现同步的两种锁机制,用于保护共享资源在多线程环境下的访问。它们的设计目的是为了在不同情况下提供更高效的并发控制。

        synchronized 是轻量级锁也是重量级锁。

        2.1 真正加锁的底层逻辑顺序

        CPU 提供了原子操作指令给操作系统,操作系统提供了 mutex 互斥锁给 JVM ,接着 JVM 将锁封装成了 synchronized 中的悲观锁、ReenTrantLock 等。

        2.2 轻量级锁

        加锁机制尽可能不适用 mutex ,而是尽量在用户态代码中完成,实在锁竞争太大、锁冲突太大了,再转换为重量级,即使用 mutex 。

        轻量级锁是一种乐观锁机制,用于优先低竞争情况下的同步操作。当一个线程尝试获取锁是,如果锁没有被其他线程占用,会将对象的 Mark Word 指向当前线程,将对象状态标记为“偏向锁”。

        2.3 重量级锁

        加锁机制重度依赖了 OS 提供了 mutex ,大量的内核态用户态切换,很容易引发线程的调度。两个操作成本比较高,一旦涉及到用户态和内核态的切换,就意味着“沧海桑田”。

        重量级锁是一种悲观锁机制,用于处理高竞争情况下的同步操作。当多个线程竞争同一把锁时,会将锁升级为重量级锁,线程会被阻塞,进入阻塞状态。

        2.4 区别于适用场景

        synchronized 开始是一个轻量级锁。如果锁冲突比较严重,就会变成重量级锁。

        轻量级锁:适用于低竞争情况下的同步操作,提高了并发性能,但在高竞争情况下会升级为重量级锁。

        重量级锁:适用于高竞争情况下的同步操作,保证了数据的一致性,但在低竞争情况下会带来额外的开销。

在实际应用中,Java虚拟机会根据当前线程的竞争情况动态地选择轻量级锁或重量级锁来进行同步操作,以提高系统的并发性能和数据一致性。

        2.5 轻量级锁与乐观锁的区别、重量级锁与悲观锁的区别

轻量级锁:

        轻量级锁是一种乐观锁,它尝试使用 CAS(Compare and Swap)等原子操作来尝试获取锁,避免了线程阻塞和内核态操作,因此被称为轻量级。如果 CAS 操作成功,线程就成功获取了锁,如果失败,则会升级为重量级锁或其他适合的锁机制。轻量级锁并不是没有真正的加锁,而是通过乐观的方式尝试获取锁,避免了一些开销较大的操作。

重量级锁:

        重量级锁是一种悲观锁,它通常会涉及到线程的阻塞、唤醒和操作系统的调度等操作,因此被称为重量级。当多个线程竞争锁时,重量级锁会导致线程进入阻塞状态,等待其他线程释放锁后才能继续执行。重量级锁会涉及到真正的加锁操作,包括线程的阻塞和唤醒等。

        3.0 自旋锁与挂起等待锁概述

        自旋锁和挂起等待锁是两种不同的锁机制,它们在处理线程同步和互斥时有不同的实现方式和特点。

        synchronized 是自旋锁也是挂起锁。

        3.1 自旋锁(Spin Lock)

        自旋锁是一种基于忙等待的锁机制,当一个线程尝试获取锁时,如果发现锁已经被其他线程占用,它会一直循环检查锁的状态(自旋)直到锁可用。自旋锁适用于锁被占用时间较短的情况,因为它可用减少线程切换的开销。但是如果锁被长时间占用,自旋锁会导致线程长时间占用 CPU 资源而无法进展,造成性能问题。

        简单来说,自旋锁一直会占用 CPU 资源,所谓的“空转”、“忙等待”,只要锁被释放了,那么自旋锁就会立马获取锁,效率高。

        按照之前的方式,线程再抢锁失败后,进入阻塞状态,放弃 CPU ,需要过很久再次被调度,但实际上,大部分情况下,虽然当前线程抢锁失败,但过不了多久,锁就会被释放。这样就没有必要放弃 CPU 资源。这时候就可以适用自旋锁来处理这样的问题。

        3.2 挂起等待锁(Suspend-Resume Lock)

        挂起等待锁是一种基于线程阻塞和唤醒的锁机制,当一个线程尝试获取锁时,如果发现锁已经被占用,它会被挂起阻塞等待其他线程释放锁。当锁可用时,其他线程会唤醒被挂起的线程继续执行。

        挂起等待锁适用于锁被占用时间长的情况,因为它可以避免线程忙等待占用 CPU 资源,但是会引入线程切换的开销。

        3.3 自旋锁是一种典型的轻量级锁的实现方式

        synchronized 中的轻量级锁策略大概率就是通过自旋锁的方式实现的。

优点:没有放弃 CPU ,不涉及线程阻塞和调度,一旦锁被释放,就能第一时间获取到锁。

缺点:如果锁被其他线程持有的时间比较长,那么就会持续的消耗 CPU 资源。(而挂起等待的时候是不会消耗 CPU 资源的)。

        相对应的,挂起等待锁是一种典型的重量级锁的实现方式。

        4.0 公平锁与非公平锁概述

        公平锁和非公平锁是两种不同的锁策略,它们主要影响了锁的获取顺序和公平性。

        synchronized 是非公平锁。ReenTrantLock 默认是非公平锁,可以转换成公平锁。

ReentrantLock fairLock = new ReentrantLock(true); // 创建公平锁

        通过将参数设置为 true,可以创建一个公平锁;而默认情况下参数为 false,创建的是非公平锁。

        4.1 公平锁(Fair Lock)

        公平锁是一种保证锁的获取按照请求的顺序进行的锁。当一个线程请求一个公平锁时,如果锁当前被其他线程占用,该线程会进入等待队列,按照先来先服务的原则等待获取锁。当锁释放时,等待时间最长的线程会被唤醒并获取锁。公平锁能够保证线程按照请求的顺序获取锁,避免了线程饥饿的问题。

        4.2 非公平锁(Unfair Lock)

        非公平锁是一种允许锁获取竞争策略,它允许新请求的线程直接尝试获取锁,而不考虑等待队列中的线程顺序。如果锁当前被其他线程占用,新请求的线程会直接尝试获取锁,而不会进入等待队列。这种策略可能会导致某些线程长时间无法获取锁,造成线程饥饿的问题。

        5.0 可重入锁与不可重入锁概述

        可重入锁的字面意思是“可以重新进入的锁”,即允许同一个线程多次获取同一把锁。比如一个递归函数里面加锁操作,递归过程中这个锁会阻塞自己吗?如果不会,那么这个锁就是可重入锁(因为这个原因可重入锁也叫做递归锁)。

        Java 里只要以 ReenTrant 开头命名的锁都是可重入锁,而且 JDK 提供的所有现成的 Lock 实现类,包括 synchronized 关键字锁都是可重入锁。

        而 Linux 系统提供的 mutex 是不可重入锁。

        6.0 读写锁与互斥锁概述

        读写锁简单来说,读操作与读操作并发执行中不会加锁,读操作与写操作并发操作中会加锁,写操作与写操作并发操作中会加锁。

        互斥锁简单来说,无论进行哪一种操作,并发执行的操作都需要加上锁。

        synchronized 不是读写锁,是互斥锁。

        ReenTrantLock 不是读写锁,是互斥锁。

ReenTrantLock 使用代码演示:

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();private int sharedData = 0;public void incrementData() {lock.lock();try {sharedData++;} finally {lock.unlock();}}public static void main(String[] args) {ReentrantLockExample example = new ReentrantLockExample();// 创建多个线程并发执行 incrementData 方法Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.incrementData();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.incrementData();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final shared data value: " + example.sharedData);}
}

        6.1 读写锁(Read-Write Lock)

        读写锁允许多个线程同时读取共享资源,但在有写操作时需要互斥访问。

读操作:

        多个线程可以同时获取读锁,并发执行读操作,不会互斥。

写操作:

        写锁是互斥的,即写操作与任何其他操作或写操作都是互斥的。当有线程持有写锁时,其他线程无法获取读锁或者写锁,直到写操作释放写锁。

        ReentrantReadWriteLock.ReadLock 类表示一个读锁,这个对象提供了 lock/unlock 方法进行加锁解锁。

        ReentrantReadWriteLock.WriteLock 类表示一个写锁,这个对象提供了 lock/unlock 方法进行加锁解锁。

代码如下:

import java.util.concurrent.locks.ReentrantReadWriteLock;public class MyReentrantLock {private int data = 100;private final ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();private final ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();private final ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();public int readData(){readLock.lock();try {return data;}finally {readLock.unlock();}}public void writeData(int data){writeLock.lock();try {this.data = data;}finally {writeLock.unlock();}}
}
public class demo1 {public static void main(String[] args) {MyReentrantLock myReentrantLock = new MyReentrantLock();//读取数据for (int i = 0; i < 100; i++) {new Thread(()->{System.out.println(Thread.currentThread().getName() + "-->读取数据为: " + myReentrantLock.readData());}).start();}//写数据for (int i = 0; i < 100; i++) {new Thread(()->{myReentrantLock.writeData(1);}).start();}for (int i = 0; i < 100; i++) {new Thread(()->{System.out.println(Thread.currentThread().getName() + "-->读取数据为: " + myReentrantLock.readData());}).start();}}
}

        6.2 互斥锁(Mutex Lock)

        互斥锁是一种常见的锁机制,用于保护共享资源的互斥访问。无论是读操作还是写操作,都要获取互斥锁才能访问共享资源。

        7.0 CAS 概述

        CAS(Compare and Swap)是一种并发控制机制,通常用于实现无锁算法。它主要用于解决多线程并发访问共享数据时的原子性操作问题。

        CAS 的基本原理是利用 CPU 提供的原子性指令来实现无锁的原子操作。当多个线程同时尝试执行 CAS 操作时,只有一个线程会成功,其他线程会失败并重试。

CAS 操作包括三个步骤:

        1)比较(Compare):首先, CAS 会比较当前内存中的值和预期值是否相等。

        2)交换(Swap):如果相等,CAS 就会将新值写入到主内存中;否则,不做任何操作。

        3)返回(Return):CAS 操作会返回操作是否成功的结果,通常时一个布尔值。

        这三个操作都是原子性的,因此不存在线程安全问题。

图解:

        7.1 CAS 的实际应用

        原子操作、非阻塞算法、自旋锁、ABA 问题的解决、乐观锁的实现等。

        7.1.1 CAS 的实际应用 - 实现原子类

        CAS 可以用于实现原子操作,比如 AtomicInteger、AtomicLong 等原子类都是基于 CAS 实现的。在多线程环境下,通过 CAS 可以确保对共享变量的操作是原子的,避免了使用锁带来的性能开销。

        比如说 AtomicInteger 类,是基于 CAS 的思想实现的,假如有一个 AtomicInteger 的实例对象,在多线程中,对该实例对象进行 +1 操作,一般来说,如果不加锁的话,会出现线程安全问题,但是对于当前对象来说,即使不用加锁,也不用出现线程安全问题。

代码如下:

import java.util.concurrent.atomic.AtomicInteger;public class MyAtomicInteger {private AtomicInteger count = new AtomicInteger(0);public void add(){System.out.println(count.incrementAndGet());}
}
public class Text {public static void main(String[] args) {MyAtomicInteger myAtomicInteger = new MyAtomicInteger();for (int i = 0; i < 1000; i++) {new Thread(()->{for (int j = 0; j < 5000; j++) {myAtomicInteger.add();}}).start();}}
}

运行结果如下:

        运行结果是正确的,没有出现线程安全问题。

        这是为什么即使没有加上锁也不会出现线程安全问题呢?

        答案就在用了 AtomicInteger 修饰的变量,且 +1 操作用到了 count.incrementAndGet() 实例方法。

详细对 count.incrementAndGet() 方法进行分析:

        该方法中还包含了 getAndAddInt() 方法,第一个参数是代表着当前对象,第二个参数可以认为是存放值的地址,第三个参数默认为 1 。

进入 getAndAddInt() 方法进行分析:

         参数 o 代表着当前对象,参数 offset 代表着值的地址,参数 delta 为 1 。该方法中内部定义了一个变量 v ,通过 getIntVolatile() 这个方法,用当前的对象还有值的地址获取到最新的数据赋值给 v 。再接着通过 weakCompareAndSetInt() 方法,来比较当前的 v 跟之前获取的 v 的值是否相同,如果相同,代表着没有线程访问这个数据,只有当前线程正在访问,那么就可以对这个数据进行修改,再返回到主内存中;如果不相同,代表有其他线程访问这个数据,此时不能直接将当前线程更新的值放到主内存中,会出现线程安全问题,因此重复循环,再来新一轮,先获取主内存中最新的数据,在来比较当前数据与之前获取到的 v 是否相同...一直循环往复。直到当前数据与之前的获取到的 v 相同,那么就可以将值放入到内存中。

进入 weakCompareAndSetInt() 方法进行分析:

        如果成功就返回 true,否则返回 false 。 

        最后,可以清楚的了解到以上这个思想跟 CAS 的机制是一致的。

        7.1.2 CAS 的实际应用 - 自旋锁

        自旋锁是基于 CAS 机制实现更灵活的锁,获取到更多的控制权。

伪代码:

public class MySpinLock {private  Thread ower = null;public void lock(){//通过 CAS 看当前锁是否被某个线程持有//如果这个锁已经被别的线程持有,那么就自旋等待。//如果这个锁没有被别的线程持有,那么就把 ower 设为当前尝试加锁的线程。while (!CAS(this.ower,null,Thread.currentThread())){}}public void unlock(){this.ower = null;}
}

        结合自旋锁的特点和 CAS 机制来分析,线程只要没有获取的锁,就会一直占用 CPU 资源等待,直到锁释放为止,如何来判断锁是否被占用呢?

        就可以通过 CAS 机制来判断,大概流程是:判断当前的线程 ower 是否为 null ,如果是,则将 ower 修改为当前线程所持有,这样来看,其他线程也会通过 CAS 机制来判断当前的 ower 是否否为 null ,返回结果为 false ,则只能空转了,等待当前线程释放锁,此时释放锁会把 ower 赋值为 null 。交给其他线程来获取这把“锁”。 

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

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

相关文章

C++高级特性:柯里化过程与std::bind(六)

1、柯里化过程 1.1、operator()的引入 现在需要完成这样一个需求&#xff1a;有一个函数每次调用返回的结果不一样。例如&#xff1a;两次调用的返回值都不一样那么就可以达到这种目的 1.1.1、简单点的写法 可以给一个全局的变量&#xff08;静态变量&#xff09;&#xff…

深入理解JVM垃圾收集器

相关系列 深入理解JVM垃圾收集算法-CSDN博客 目前市面常见的垃圾收集器有Serial、ParNew、Parallel、CMS、Serial Old、Parallel Old、G1、ZGC以及有二种不常见的Epsilon、Shenandoah的&#xff0c;从上图可以看到有连线的的垃圾收集器是可以组合使用&#xff0c;是年轻代老年代…

Input DropDown 拼接成 select组件(基于antd和react)

前言&#xff1a;为什么不直接用select&#xff0c;还要舍近求远搞inputdropdown这种缝合怪&#xff0c;是因为antd的select不支持选中项再编辑&#xff0c;效果如图 比如&#xff1a;选中的lucy文案变成了placeholder不能再编辑了 封装此组件虽然比较简单&#xff0c;但还是有…

PLC互连全攻略:Profinet和EthernetIP实操演示

在今日的技术分享中&#xff0c;将详细探讨实现Profinet和Ethernet/IP的通信配置&#xff0c;以连接西门子PLC&#xff08;Profinet&#xff09;和罗克韦尔PLC&#xff08;Ethernet/IP&#xff09;。本篇将重点介绍专为通信而设计的Profinet转Ethernet/IP网关&#xff0c;在联接…

ActiveMQ介绍及linux下安装ActiveMQ

ActiveMQ介绍 概述 ActiveMQ是Apache软件基金下的一个开源软件&#xff0c;它遵循JMS1.1规范&#xff08;Java Message Service&#xff09;&#xff0c;是消息队列服务&#xff0c;是面向消息中间件&#xff08;MOM&#xff09;的最终实现&#xff0c;它为企业消息传递提供高…

【R语言从0到精通】-3-R统计分析(列联表、独立性检验、相关性检验、t检验)

上两次教程集中学习了R语言的基本知识&#xff0c;那么我们很多时候使用R语言是进行统计分析&#xff0c;因此对于生物信息学和统计科学来说&#xff0c;R语言提供了简单优雅的方式进行统计分析。教程参考《Rlearning》 3.1 描述性统计分析 3.1.1 载入数据集及summary函数 我…

【力扣题】关于单链表和数组习题

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

(Java)数据结构——图(第五节)Kruskal的实现最小生成树(MST)

前言 本博客是博主用于复习数据结构以及算法的博客&#xff0c;如果疏忽出现错误&#xff0c;还望各位指正。 Kruskal算法&#xff08;Kruskal的实现原理&#xff09; Kruskal算法的原理&#xff1a; 就是每次取最小的边&#xff0c;看看是不是与已经选择的构成回路&#x…

金融机构面临的主要AI威胁:身份伪造统与社会工程攻击

目录 攻击者利用AI威胁的过程 金融机构如何防范AI攻击 针对AI欺诈的解决方案 2023年11月&#xff0c;诈骗分子伪装成某科技公司郭先生的好友&#xff0c;骗取430万元&#xff1b;2023年12月&#xff0c;一名留学生父母收到孩子“被绑架”的勒索视频&#xff0c;被索要500万元赎…

ISTQB选择国内版,还是国际版呢

1, ISTQB简介 ISTQB&#xff08;International Software Testing Qualifications Board&#xff09;是一个国际软件测试资格认证机构&#xff0c;旨在提供一个统一的软件测试认证标准。ISTQB成立于2002年&#xff0c;是非盈利性的组织&#xff0c;由世界各地的国家或地区软件测…

Qt5 编译oracle数据库

库文件 1、Qt源码目录&#xff1a;D:\Qt5\5.15.2\Src\qtbase\src\plugins\sqldrivers\oci 2、oracle客户端SDK: https://www.oracle.com/database/technologies/instant-client/winx64-64-downloads.html 下载各版本中的如下压缩包&#xff0c;一定要版本相同的 将两个压缩包…

事务,MySQL函数和索引详解

文章目录 事务简介提交方式手动提交事务 事务执行流程修改事务的默认提交方式 事务原理四大特性隔离级别 MySQL函数常见的日期函数判断函数case when字符串函数数字函数 MySQL性能(了解)索引概念分类MySQL索引语法数据结构(了解)BTreeBTree好处 优缺点优势劣势 创建原则 事务简…

c++取经之路(其五)——类和对象拷贝构造函数

概念&#xff1a;拷贝构造函数&#xff0c;只有单个形参&#xff0c;该形参是对本类类型对象的引用(一般常用const修饰)&#xff0c;在用已存在的类类型对象创建新对象时由编译器自动调用。 特征&#xff1a; 1. 拷贝构造函数是构造函数的一个重载形式 如&#xff1a; 2. 拷贝…

基于springboot实现中小型医院网站管理系统【项目源码+论文说明】

基于springboot实现中小型医院网站管理系统演示 摘要 本基于Spring Boot的中小型医院网站设计目标是实现用户网络预约挂号的功能&#xff0c;同时提高医院管理效率&#xff0c;更好的为广大用户服务。 本文重点阐述了中小型医院网站的开发过程&#xff0c;以实际运用为开发背…

[数据结构]——二叉树——堆的实现

1. 堆的概念及结构 如果有一个关键码的集合K { &#xff0c; &#xff0c; &#xff0c;…&#xff0c; }&#xff0c;把它的所有元素按完全二叉树的顺序存储方式存储 在一个一维数组中&#xff0c;并满足&#xff1a; < 且 < ( > 且 > ) i 0&#xff0c;1&…

rocky9 yum 安装与配置MySQL8

1.前置条件&#xff1a; 把yum包更新到最新 [rootlocalhost ~]# yum update 查看系统中是否已安装 MySQL 服务 rpm -qa|grep mysql 如果有安装mysql,则需要先卸载之前安装的mysql&#xff1a;yum -y remove mysql 然后再查看mysql是否都卸载完成,如果还有没卸载完成的&am…

小程序开发SSL证书下载和安装

在开发小程序时&#xff0c;确保数据的安全传输至关重要&#xff0c;而实现这一目标的关键在于正确获取与安装SSL证书。以下详细介绍了从获取到安装SSL证书的完整流程&#xff0c;以助您为小程序构建可靠的加密通信环境。 一、小程序SSL证书类型选择&#xff1a; 域名验证型D…

Linux:软件包管理器 - yum

Linux&#xff1a;软件包管理器 - yum Linux的软件安装方式源代码安装rpm包安装yum安装 yum三板斧yum listyum installyum remove yum生态yum源 Linux的软件安装方式 源代码安装 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序 源代码安…

git知识

如何将develop分支合并到master分支 #简单版 git checkout master git pull origin master git merge origin/develop # 解决可能的冲突并提交 git push origin master#复杂版 git checkout master # 拉取远程 master 分支的最新代码并合并到本地 git pull origin master # 拉…

新时代·高质量·硬道理丨开放的大门越开越大、开放的水平越来越高

新时代下&#xff0c;中国坚定不移地实施扩大高水平对外开放战略&#xff0c;致力于构建更高层次、更宽领域的开放型经济体系。以下是对新时代高质量硬道理这一主题下&#xff0c;中国开放大门越开越大、开放水平越来越高的几个关键点分析&#xff1a; 全方位开放格局 政府工…