并发编程三要素:可见性、原子性、有序性

news2024/11/15 13:41:17

一、介绍

1、什么是可见性、原子性、有序性?

  1. 可见性(visibility):指一个线程对共享变量的修改能够被其他线程立即看到的特性。在多线程环境下,如果一个线程修改了一个共享变量的值,那么其他线程可能无法立即看到这个修改,因为线程之间有可能存在缓存不一致的问题。为了保证可见性,可以使用volatile关键字或者显式地使用锁来实现。

  2. 原子性(atomicity):指一个操作是不可分割的、完整的,要么全部执行成功,要么全部不执行,不存在执行一半的情况。在多线程环境下,如果一个操作不是原子性的,那么可能会发生竞态条件(race condition)等问题,导致程序出现不可预期的错误。为了保证原子性,可以使用synchronized关键字或者使用Atomic类中提供的原子操作。

例如:在java中count++ 与 Person person = new Person()就不具备原子性,因其在JVM中会变成多个指令顺序执行

  1. 有序性(ordering):指程序执行的顺序必须符合预期,不能出现乱序的情况。在多线程环境下,由于编译器、处理器、缓存等因素的影响,程序执行的顺序可能会出现不一致的情况,导致程序出现错误。为了保证有序性,可以使用volatile关键字或者显式地使用锁来实现。同时,Java提供了happens-before规则,它可以保证在特定情况下,操作的顺序是按照预期的顺序执行的。

2、举例说明

1、可见性

public class Main {
    public static void main(String[] args)  throws InterruptedException {
        HelloThread t = new HelloThread();
        t.start();
        Thread.sleep(1);
        t.running = false; // 标志位置为false
    }
}

class HelloThread extends Thread {
    public volatile boolean running = true;
    public void run() {
        int n = 0;
        while (running) {
            n ++;
            System.out.println(n + " hello!");
        }
        System.out.println("end!");
    }
}

注意到HelloThread的标志位boolean running是一个线程间共享的变量。线程间共享变量需要使用volatile关键字标记,确保每个线程都能读取到更新后的变量值。

为什么要对线程间共享的变量用关键字volatile声明?这涉及到Java的内存模型。在Java虚拟机中,变量的值保存在主内存中,但是,当线程访问变量时,它会先获取一个副本,并保存在自己的工作内存中。如果线程修改了变量的值,虚拟机会在某个时刻把修改后的值回写到主内存,但是,这个时间是不确定的!

┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
           Main Memory
│                               │
   ┌───────┐┌───────┐┌───────┐
│  │ var A ││ var B ││ var C │  │
   └───────┘└───────┘└───────┘
│     │ ▲               │ ▲     │
 ─ ─ ─│─│─ ─ ─ ─ ─ ─ ─ ─│─│─ ─ ─
      │ │               │ │
┌ ─ ─ ┼ ┼ ─ ─ ┐   ┌ ─ ─ ┼ ┼ ─ ─ ┐
      ▼ │               ▼ │
│  ┌───────┐  │   │  ┌───────┐  │
   │ var A │         │ var C │
│  └───────┘  │   │  └───────┘  │
   Thread 1          Thread 2
└ ─ ─ ─ ─ ─ ─ ┘   └ ─ ─ ─ ─ ─ ─ ┘

这会导致如果一个线程更新了某个变量,另一个线程读取的值可能还是更新前的。例如,主内存的变量a = true,线程1执行a = false时,它在此刻仅仅是把变量a的副本变成了false,主内存的变量a还是true,在JVM把修改后的a回写到主内存之前,其他线程读取到的a的值仍然是true,这就造成了多线程之间共享的变量不一致。

因此,volatile关键字的目的是告诉虚拟机:

  • 每次访问变量时,总是获取主内存的最新值;
  • 每次修改变量后,立刻回写到主内存。

volatile关键字解决的是可见性问题:当一个线程修改了某个共享变量的值,其他线程能够立刻看到修改后的值。

如果我们去掉volatile关键字,运行上述程序,发现效果和带volatile差不多,这是因为在x86的架构下,JVM回写主内存的速度非常快,但是,换成ARM的架构,就会有显著的延迟。

2、原子性

原子性也是大家最常见的使用场景,即保证多线程安全操作;

在java语义中,有些操作是天生具备原子性,如下:

2.1、局部变量

Java中的局部变量只在方法的作用域内可见,只有当前线程可以访问它,因此局部变量天生具备线程安全性。

举个例子,假设有一个方法calcSum,用于计算从1到n的整数之和

public int calcSum(int n) {
    int sum = 0; // 局部变量sum
    for (int i = 1; i <= n; i++) {
        sum += i;
    }
    return sum;
}

sum是一个局部变量,它只在calcSum方法内部可见。在多线程环境下,每个线程都会拥有自己的执行栈和局部变量表,因此不会出现线程间共享变量的情况。每个线程都可以独立的执行calcSum方法,不会互相影响,因此该方法是线程安全的。

需要注意的是,如果方法中使用了共享变量(比如类的成员变量或静态变量),那么就需要进行线程同步操作,以保证线程安全性。

2.2、单原子操作

JVM规范定义了几种原子操作:

  • 基本类型(longdouble除外)赋值,例如:int n = m
  • 引用类型赋值,例如:List<String> list = anotherList
//单条原子操作的语句不需要同步。例如:
public void set(String s) {
    this.value = s;
}

//对引用也是类似。例如:
public void set(String s) {
    this.value = s;
} 

不具备原子性的操作:

2.3、共享变量
public class SharedVariable {
    private int count = 0;

    public void increment() {
        count++;
    }

    public void decrement() {
        count--;
    }

    public int getCount() {
        return count;
    }
}

在这个示例代码中,incrementdecrement 方法对共享变量 count 进行递增和递减操作,但这些操作不具备原子性。如果多个线程同时调用这些方法,可能会出现竞态条件(race condition)导致计数器的值出现错误。

2.4、复合操作
public static Person person;

public Persion getPerson(String name, int age) {
  	person = new Person(name, age);
 		return person;
}

代码中的 getPerson 方法是线程不安全的,因为它对共享变量 person 进行了非原子性的读写操作。

多个线程同时调用 getPerson 方法时,可能会出现竞态条件(race condition),导致 person 变量的值出现错误。例如,一个线程在执行 person = new Person(name, age) 语句时,另一个线程可能会读取到 person 变量的旧值,导致返回的 Person 对象不是最新创建的对象。

3、有序性

单例模式中双重校验的使用

public class Singleton {
    private volatile static Singleton uniqueInstance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

在上面的代码中,volatile变量是为了解决程序的重排序问题,原因如下:

uniqueInstance = new Singleton() 这行代码并不是一个原子指令。使用 javap -c指令,可以快速查看字节码。

// 创建 Cache 对象实例,分配内存
0: new           #5                  // class com/query/Cache
// 复制栈顶地址,并再将其压入栈顶
3: dup
// 调用构造器方法,初始化 Cache 对象
4: invokespecial #6                  // Method "<init>":()V
// 存入局部方法变量表
7: astore_1

从字节码可以看到创建一个对象实例,可以分为三步:

  1. 分配对象内存
  2. 调用构造器方法,执行初始化
  3. 将对象引用赋值给变量。

虚拟机实际运行时,以上指令可能发生重排序。以上代码 2,3 可能发生重排序,但是并不会重排序 1 的顺序,排序后的顺序如下:

  1. 分配对象内存
  2. 将对象引用赋值给变量**(此时uniqueInstance就不等于null了,但uniqueInstance并没有初始化!)**
  3. 调用构造器方法,执行初始化

如果出现重排序问题,此时变量也没有使用volatile修饰,那么该双重校验模式会出现异常,如下图:

在这里插入图片描述

故正确的双重检查锁定模式需要需要使用 volatilevolatile主要包含两个功能

  1. 保证可见性。使用 volatile 定义的变量,将会保证对所有线程的可见性。
  2. 禁止指令重排序优化。

由于 volatile 禁止对象创建时指令之间重排序,所以其他线程不会访问到一个未初始化的对象,从而保证安全性。

二、并发编程中常见关键字

在并发编程中常见以下几种关键字,他们中有些只具备三要素中的一种或两种,故在使用时开发人员要十分清楚不同关键字的使用场景,避免出现并发编程错误

1、volatile

2、synchronized、Lock

3、java.util.concurrent.atomic

4、static

三、先说结论

1、volatile关键字修饰的变量具有可见性、有序性和部分原子性。

可见性:当一个线程修改了volatile变量的值,该值会立即被写回主内存,同时其他线程在读取该变量时也会直接从主内存中读取,而不是从线程私有的内存中读取。因此,对volatile变量的修改对其他线程是可见的。

有序性:Java内存模型(JMM)定义了一些规则来保证多线程之间的操作顺序,volatile变量的读写操作会遵循这些规则,保证了读操作和写操作的顺序性,避免了出现一些奇怪的现象,比如指令重排序等。

部分原子性:volatile变量的单次读/写操作是原子性的(例如count=1赋值操作),但对于复合操作(例如i++)则不保证原子性。要实现原子性操作,可以使用synchronized关键字或者Lock来进行同步控制。

注意:虽然volatile关键字可以保证可见性、有序性和部分原子性,但并不能完全替代锁。在需要实现复杂的操作时,还是需要使用synchronized关键字或者Lock来进行同步控制。

2、synchronized、Lock具备可见性、有序性、原子性

synchronizedLock 锁都具备可见性、有序性和原子性。

  • 可见性:对于 synchronizedLock 锁而言,都能够保证在一个线程修改了共享变量的值之后,其它线程能够立即看到这个变量的最新值。在 synchronized 中,进入和退出同步块时,会自动执行锁的获取和释放操作,这些操作会将本地缓存中的数据刷新到主内存中,从而保证了可见性。在 Lock 锁中,当一个线程获取锁时,它会读取主内存中的最新值,将其存储到本地内存中,当它释放锁时,会将本地内存中的数据刷新到主内存中,从而保证了可见性。

  • 有序性:对于 synchronizedLock 锁而言,都能够保证在一个线程执行完毕后,其它线程才能执行被锁保护的代码块或方法。在 synchronized 中,对于同一个锁对象,同一时间只有一个线程能够执行被锁保护的代码块或方法,其它线程需要等待锁的释放,从而保证了执行的有序性。在 Lock 锁中,当一个线程获取锁时,其它线程需要等待锁的释放,从而保证了执行的有序性。

  • 原子性:对于 synchronizedLock 锁而言,都能够保证其中的操作是原子性的。当一个线程获得了锁,进入了被锁保护的代码块或方法,其它线程需要等待,直到这个线程执行完毕,释放锁之后,其它线程才能进入被锁保护的代码块或方法。这样就可以保证其中的操作是原子性的,避免了多个线程同时修改共享变量的值,导致数据出现错误的情况。

注意:在使用 Lock 锁时,需要手动进行锁的获取和释放操作,这就需要更加精细的控制,否则可能会导致死锁等问题。在 synchronized 中,锁的获取和释放是自动进行的,因此更加方便使用。而在性能上,Lock 锁通常比 synchronized 更加高效。

3、java.util.concurrent.atomic 包具备可见性、原子性、有序性

java.util.concurrent.atomic 下的类都具备可见性、有序性和原子性。这里以AtomicInteger举例:

  • 可见性:在多线程环境中,当一个线程修改了 AtomicInteger 对象的值后,其他线程可以通过 get() 方法获取到最新的值,这是因为 AtomicInteger 内部使用了 volatile 修饰的 value 变量,确保了其对多线程的可见性。

  • 原子性:AtomicInteger是Java中的一个原子类,提供了一种线程安全的整数类型。在多线程环境下,对于AtomicInteger的操作都是原子性的,即多个线程同时对一个AtomicInteger进行操作时,不会出现数据不一致的情况。

  • 有序性:java.util.concurrent.atomic 包下的类,例如 AtomicIntegerAtomicLong 等,都使用了 CAS(Compare And Swap)操作来保证有序性。CAS 操作包括三个操作数,分别是内存位置 V、期望值 A 和新值 B。如果当前内存位置的值等于期望值 A,则将该位置的值更新为新值 B。如果当前内存位置的值不等于期望值 A,则不做任何操作。通过使用 CAS 操作,能够保证操作的原子性,同时也能够保证操作的有序性。

注意: AtomicInteger提供的原子性只适用于单个操作,对于多个操作的复合操作,仍然需要使用synchronized关键字或者Lock来进行同步控制,以保证原子性和线程安全。

4、static具备部分原子性,但不具备可见性、有序性

static 修饰的变量具备可见性和有序性,但不具备原子性。

  • 可见性:static 修饰的静态变量在所有实例之间共享,因此对于一个类的所有实例来说,静态变量是可见的。但是这不代表多线程环境下的可见性!为了确保多线程环境下的可见性,需要使用 volatile 修饰符。volatile 修饰的静态变量具有可见性,因为当一个线程修改该变量时,其他线程可以立即看到修改后的值。

  • 有序性:Java 内存模型确保了在单线程中的程序顺序性,但在多线程环境下,不同线程可能会看到不同的执行顺序。为了确保有序性,可以使用 synchronized 关键字或者 java.util.concurrent 包中的锁机制。

  • 部分原子性:静态变量的操作,如单赋值[不涉及创建+赋值]和读取,通常是原子性的。但是,复合操作(例如自增)不具有原子性。在多线程环境下,为了确保原子性,您可以使用 synchronized 关键字或者 java.util.concurrent.atomic 包中的原子类(例如 AtomicInteger)。

注意: static 修饰的变量本身不具备可见性、有序性和原子性。为了确保这些特性,需要结合使用其他关键字和工具,如 volatilesynchronized 或者 java.util.concurrentjava.util.concurrent.atomic 包中的类。

四、实际使用

1、volatile

在实际使用中volatile大部分用于保证程序的有序性和可见性

有序性使用:单例模式中的双重校验模式 (参考: 有序性)

部分原子性使用:(参考: 可见性)

可见性使用:多线程环境下对变量的读写立即可见 (参考: 可见性)

2、synchronized、Lock

在实际使用中这两种都用于保证组合操作的原子性

  • 使用 synchronized:
public class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

在这个示例中,我们使用 synchronized 关键字对 increment()getCount() 方法进行同步化,以保证每次只有一个线程能够访问这些方法,从而保证程序的有序性和原子性。同时,由于 count 变量是类变量,因此它具备可见性,即当一个线程修改了 count 变量的值之后,其它线程能够立即看到这个变量的最新值。

  • 使用 Lock:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Counter {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

在这个示例中,我们使用 java.util.concurrent.locks.Lock 接口和 ReentrantLock 类对 increment()getCount() 方法进行同步化,以保证每次只有一个线程能够访问这些方法,从而保证程序的有序性和原子性。与 synchronized 不同的是,使用 Lock 可以更灵活地控制锁的获取和释放,同时也可以实现更细粒度的锁控制。由于 count 变量是类变量,因此它具备可见性,即当一个线程修改了 count 变量的值之后,其它线程能够立即看到这个变量的最新值。

3、java.util.concurrent.atomic

使用 java.util.concurrent.atomic 包下的类可以很方便地实现能够保证程序有序性、原子性、可见性的操作,个示例代码:

import java.util.concurrent.atomic.AtomicInteger;

public class Counter {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet();
    }

    public int getCount() {
        return count.get();
    }
}

在这个示例中,我们使用 java.util.concurrent.atomic.AtomicInteger 类对 count 变量进行同步化,以保证程序的有序性、原子性和可见性。AtomicInteger 类提供了一系列原子操作方法,例如 incrementAndGet()get() 方法,可以保证多线程环境下对 count 变量的操作是原子性的,并且可以保证线程间对变量的修改具有可见性。由于 count 变量是类变量,因此它具备可见性,即当一个线程修改了 count 变量的值之后,其它线程能够立即看到这个变量的最新值。

使用 java.util.concurrent.atomic 包下的类可以很方便地实现线程安全的操作,同时也可以避免使用 synchronized 和 Lock 等同步机制所带来的性能开销

4、static

在Java中,使用 static 关键字可以创建静态变量和方法。静态变量和方法属于类而不是对象,因此它们在内存中只有一份副本,并且可供所有实例访问。

但在多线程编程中,我们可以使用 static 变量和方法来无法确保程序的有序性、原子性和可见性,故常见操作是通过volatile、synchronized等方式实现,示例如下:

public class Counter {
    private static volatile int count = 0;

    public static synchronized void increment() {
        count++;
    }

    public static synchronized int getCount() {
        return count;
    }
}

在这个示例中,我们使用 static 关键字将 count 变量设置为类变量,从而保证该变量在整个类范围内是唯一的。同时,我们还使用 volatile 关键字对 count 变量进行修饰,以保证线程间对变量的修改具有可见性。

increment()getCount() 方法中,我们使用 synchronized 关键字对这些方法进行同步化,以保证每次只有一个线程能够访问这些方法,从而保证程序的有序性和原子性。由于 count 变量是类变量,因此它具备可见性,即当一个线程修改了 count 变量的值之后,其它线程能够立即看到这个变量的最新值。

使用 static 关键字可以方便地将变量和方法绑定在类级别上,从而实现对变量和方法的全局同步,同时也能够保证线程间对变量的修改具有可见性。但是,使用 static 关键字也可能会带来一些问题,例如可能会增加内存消耗、降低程序的可扩展性等。因此,在使用 static 关键字时需要注意权衡其优缺点,选择合适的方案。

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

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

相关文章

时隔两个多月,一起来看ChatGPT现况如何?

ChatGPT这股风吹了两个多月&#xff0c;时至今日&#xff0c;各平台上与ChatGPT相关的文章&#xff0c;到现在依旧拥有着不小的流量。三月中旬上线了ChatGPT-4&#xff0c;与我们的文心一言前后脚发布&#xff0c;而后阿里的“通义千问”也展现了不俗的实力&#xff0c;那到现在…

【多线程】初识多线程

1. 为什么要学习多线程&#xff1f;首先相信各位小伙伴在学习 JavaSE 的时候&#xff0c;肯定写过一些小游戏吧&#xff0c;比如猜数字&#xff0c;关机小程序...但是如果现在要在猜数字小游戏上面加上一个功能&#xff0c;设定20秒没猜中&#xff0c;就判定游戏失败&#xff0…

数据结构:什么是堆,和二叉树有什么关系

堆栈模型 JS 代码执行时&#xff0c;值类型变量存储在栈&#xff0c;引用类型变量存储在堆。 // 变量 a 存储在栈里 let num1 1 let num2 num1 num2 2 // 这时打印 num1 是 1&#xff0c;num2 是 2。// { a: 1 } 存在堆里&#xff0c;obj1 只是一个指针引用 let obj1 { a…

华为 WATCH Ultimate 如何开通和使用北斗卫星卡

华为春季新品发布会发布了全新系列手表华为WATCH Ultimate非凡大师&#xff0c;实现了“向上捅破天”的突破性应用&#xff0c;让大家通过手表与世界一直相连。当用户在周围无信号&#xff08;无蜂窝网络/WLAN网络覆盖&#xff09;的情况下&#xff0c;处于空旷无遮挡的环境时&…

基于超声波传感器的液位测量及控制系统设计(STM32)

一、引言 随着工业的发展&#xff0c;计算机、微电子、传感器等高新技术的应用和研究&#xff0c;液位仪表的研制得到了长足的发展&#xff0c;以适应越来越高的应用要求。液位的测量在工业生产过程中已经起着相当重要的作用&#xff0c;其类型大概可以分为接触型和非接触型两大…

最近搭了一个数据监测看板。

在大数据时代&#xff08;这个开头我已经看腻了 &#xff09;&#xff0c;为了挖掘冗余数据的价值&#xff0c;数据分析需求日益增多&#xff0c;而分析结果的常见表现形式有数据分析报告和数据看板&#xff08;大屏&#xff09;&#xff0c;与报告文档不同&#xff0c;数据看板…

8.1.0:DHTMLX Suite JavaScript UI Crack

适用于现代 Web 应用程序的强大 JavaScript 小部件库 - DHTMLX 套件 用于创建现代用户界面的轻量级、快速且通用的 JavaScript/HTML5 UI 小部件库。 DHTMLX Suite 有助于推进 Web 开发和构建具有丰富功能的数据密集型应用程序。 DHTMLX Suite 是一个 UI 小部件库&#xff0c;用…

移动端高性能Unity播放器实现方案

前情提要&#xff1a; 视听体验再进化——如何在24小时内全面升级你的视频应用 如何打造新时代的终端播放产品&#xff1f; 随着VR、AR、元宇宙等新玩法的出现&#xff0c;Unity平台的视频播放需求逐渐增加&#xff0c;比如下面两个动图就是在百度真实的案例。前者是演唱会场景…

ChatGPT能够知道当下最流行的开发语言,以及各语言哪个开发框架最火吗?

如果你准备成为一名开发人员&#xff0c;但是面对琳琅满目的开发语言&#xff0c;然后每种语言的开发框架却无从下手&#xff0c;张三推荐你学这个&#xff0c;李四推荐你学那个&#xff0c;而你的时间又是有限的&#xff0c;于是我决定问一问这个万事通ChatGPT。 目录 1. 目前…

C语言小项目 -- 扫雷游戏完整代码(递归展开 + 选择标记)

目录 &#x1f680;0. 游戏介绍&#xff1a; &#x1f437; 游戏规则 &#x1f6f8;雷诀八条 &#x1f680;1. 开发环境及框架&#xff1a; &#x1f680;2. 游戏具体功能分析实现&#xff1a; &#x1f437;2.1 棋盘 &#x1f437;2.2 棋盘初始化 init_Board; &#x1…

用Abp实现两步验证(Two-Factor Authentication,2FA)登录(三):免登录验证

文章目录原理修改请求报文配置JwtBearerOptions生成Token校验Token修改认证EndPoint修改前端登录登出最终效果项目地址免登录验证是用户在首次两步验证通过后&#xff0c;在常用的设备&#xff08;浏览器&#xff09;中&#xff0c;在一定时间内不需要再次输入验证码直接登录。…

Go 语言 入门 基于 GoLand 2023.1 创建第一个Go程序

文章目录Go 语言 入门go 语言是什么?go 入门 需要 学习 什么?Go 语言有 那些优势?Go 语言 与 Java 语言 对比Go 语言 与Python 语言 对比Go 语言 基于 GoLand 创建第一个程序1. 打开 GoLand2.选择 new3. 选择SDK版本4. 输入项目名称,点击 cteate项目创建完成下一步,创建一个…

SpringBoot-心跳机制+redis实现网站实时在线人数统计

在社交网站中&#xff0c;通常需要实时统计某个网站的在线人数&#xff0c;通过该指标来实时帮助运营人员更好的维护网站业务&#xff1a; 先说一下目前在市面上主流的做法再加上我自己查阅的资料总结&#xff1a; 创建一个session监听器&#xff0c;在用户登录时即创建一个s…

WEB集群之反向代理,动静分离,NFS,mysql,MHA高可用

目录 第一章实验架构 1.1.实验图谱架构 1.2.实验前环境部署 第二章实验步骤 2.1.在ha01&#xff0c;ha02上部署keeplived,lvs-dr 2.2.Slave01,slave02安装nginx 2.3.LVS负载均衡 2.4.搭建动态网页 2.5.nginx反向代理 2.6.部署NFS 2.7.安装mysql 2.8.安装mha 2.9.主…

教你安装 CodeWhisperer: 一款个人免费的类似GitHubCopilot能代码补全的 AI 编程助手

1、官网 AI Code Generator - Amazon CodeWhisperer - AWS 官方扩展安装教程 2、安装VSCode 下载安装VSCode 3、VSCode安装CodeWhisperer插件 安装VSCode插件 - AWS Toolkit主侧栏&#xff0c;点击AWS &#xff0c;展开CodeWhisperer&#xff0c;点击Start 在下拉菜单中点…

洗浴中心管理系统【GUI/Swing+MySQL】(Java课设)

系统类型 Swing窗口类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 本系统源码地址&#xff1a; 更多系统资源库地址&#xff1a;骚戴的博客_CSDN_更多系统资源 更多系统…

煤矿井下人员精准定位系统,煤矿应急救援高效应用

煤矿行业的安全发展&#xff0c;事关数人民群众的生命财产安全。自2020年起&#xff0c;国家连续三年出台煤矿智能化建设及重大风险防控的相关指导政策&#xff0c;其中&#xff0c;2022年出台的《煤矿及重点非煤矿山重大灾害风险防控建设工作总体方案》中提到&#xff0c;要充…

52 openEuler搭建PostgreSQL数据库服务器-管理数据库角色

文章目录52 openEuler搭建PostgreSQL数据库服务器-管理数据库角色52.1 创建角色创建角色示例52.2 查看角色查看角色示例52.3 修改角色52.3.1 修改用户名52.3.2 修改用户示例52.3.3 修改用户密码52.3.4 修改角色密码示例52.4 删除角色删除角色示例52.5 角色授权角色授权示例52.6…

Python旅游好帮手:提前15天准备五一旅游景点详细数据

人生苦短&#xff0c;我用python 虽然还是有15天才放五一的假&#xff0c; 但是我的心早已经在旅游的路上了~ 本文源码&#xff1a;点击此处跳转文末名片获取 趁现在&#xff0c;先来用python做一个旅游攻略 知识点&#xff1a; requests parsel csv 第三方库&#x…

python查看时间序列数据的季节规律matplotlib画时间(10分钟为间隔)序列坐标

目录0 问题描述1. 案例12. 案例2参考资料0 问题描述 将多个时间序列数据&#xff0c;绘制到一张图上&#xff0c;每段时间序列数据一般只有几个月&#xff0c;少则 1 个月左右&#xff0c;想看它们的季节规律&#xff0c;需要去除年份&#xff0c;只看月份。 也就是横轴是1月…