说说Java“锁“ 事

news2024/10/2 6:30:00

文章目录

  • 前言
  • 大厂面试题复盘 —— 并发编程高级面试解析
  • 从轻松的乐观锁和悲观锁开讲
  • 通过8种情况演示锁运行案例,看看我们到底锁的是什么
  • 公平锁和非公平锁
  • 可重入锁(又名递归锁)
  • 死锁及排查
  • 写锁(独占锁)/读锁(共享锁)
  • 自旋锁SpinLock
  • 无锁 -> 独占锁 -> 读写锁 -> 邮戳锁
  • 无锁 -> 偏向锁 -> 轻量锁 -> 重量锁

前言

又是一年春招季,距离自己第一次在网上找Java开发的工作差不多过去一年了,这一年有大半年是在外面实习,从最开始疯狂背面经到后来在工作中慢慢理解这些概念,然后最后在实际开发中使用到具体的知识,这相当于是一个质的跃迁。
最近又在开始复习八股文和整理自己的实习经验这些,今天刚好是我们学校这个区的招聘会,看那待遇真的让人心寒,在这互联网的环境也不太好,就越发得觉得这些努力没有白费,如果很早之前就松懈了,那我也可能就只能写出这样的简历,找到这样的工作,现在在外面有实际的实习经验,相对于来说面试的时候有更多的话和面试官说了,希望不仅仅是你问我答的那种面试,更希望是一种可以切磋技术并且在工作规划人生看法能让我有不同看法或者听到不同见解的。
能坚持到现在我觉得大家首先都是优秀的,其次就是都喜欢编程的,我是在编程的时候真的会有一种很舒服的感觉,那时一心只想完成需求,那段时间总过得很快,很喜欢完成需求后获得的那种成就感,是觉得自己是真的在干一些有意义的事情:或者帮助了业务,减少重复性工作,提升了效率。
这次春招是在两三段实习后,毕设和健身间隙准备的,在大学的最后一期,在这自习室送走了22届考研,看着23届考研,现在又是一堆陌生的24届考研的同学,随着在校园熟悉的面孔越来越少,说明我们真的要毕业了。
有好长一段时间没有写博客了,这段时间有在好好工作,完成需求;有在好好生活,陪着家人;也在这间隙锻炼了身体,认识了一群相同行业的朋友。
接下来还是不能停下求职的脚步,一边学习新知识,复习总结学过的,争取都能找到一个互相心仪的公司

大厂面试题复盘 —— 并发编程高级面试解析

一、Synchronized相关问题

  1. Synchronized用过吗,其原理是什么?
  2. 你刚才提到获取对象的锁,这个"锁"到底是什么?如何确定对象的锁?
  3. 什么是可重入性,为什么说Synchronized是可重入锁?
  4. JVM对Java的原生锁做了哪些优化?
  5. 为什么说Synchronized是非公平锁?
  6. 什么是锁消除和锁粗化?
  7. 为什么说Synchronized是一个悲观锁?乐观锁的实现原理又是什么?什么是CAS,他有
  8. 乐观锁一定就是好的吗?

二、可重入锁ReentrantLock及其他显式锁相关问题

  1. 跟Synchronized相比,可重入锁ReentrantLock其实现原理有什么不同?

  2. 那么请谈谈AQS框架是怎么回事儿?

  3. 请尽可能详尽地对比下Synchronized和ReentrantLock的异同

  4. ReentrantLock是如何实现可重入性的?

  5. 你怎么理解Java多线程的?怎么处理并发?线程池有哪几个核心参数?

  6. Java加锁有哪几种锁?

  7. 简单说说lock

  8. hashmap的实现原理?hash冲突怎么解决?为什么使用红黑树?

  9. spring里面都使用了哪些设计模式?循环依赖怎么解决?

  10. 项目中哪个地方用了countdownlanch,怎么使用的?

从轻松的乐观锁和悲观锁开讲

悲观锁:认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。Synchronized关键字和Lock的实现类的都是悲观锁。

适合写操作多的场景,先加速可以保证写操作时数据正确。显示的锁定之后再操作同步资源。一句话:狼性锁

乐观锁:认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁。在Java中是通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果这个数据已经被其他线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等等

判断规则

  1. 版本号机制Version
  2. 最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的

适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。乐观锁则直接去操作同步资源,是一种无锁算法,得之我幸不得我命,再努力就是
一句话:佛系锁

两种实现方式:Version版本号机制;CAS

//伪代码说明
//====悲观锁的调用方式
public synchronized void m1(){
 //加锁后的业务逻辑
}
//====保证多个线程使用的是同一个Lock对象的前提下
ReentrantLock lock = new ReentrantLock();
public void m2(){
 lock.lock();
 try{
     //操作同步资源
 }finally{
     lock.unlock();
 }
}
//====乐观锁的调用方式
//保证多个线程使用的是同一个AtomicInteger
private AtomicInteger atomicInteger = new AtomicInteger();
atomicInteger.incrementAndGet();

通过8种情况演示锁运行案例,看看我们到底锁的是什么

锁相关的8种案例演示code

class Phone{//资源类
    public static synchronized void sendEmail(){
        try{ TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("----sendEmail");
    }
    public synchronized void sendSMS(){
        System.out.println("----sendSMS");
    }
    public void hello(){
        System.out.println("----hello");
    }
}
/**
 * @author William
 * @create 2022-07-03 18:34
 * 题目:谈谈你对多线程锁的理解,8锁案例说明
 * 口诀:线程    操作  资源类
 * 8锁案例说明:
 * 1. 标准访问有ab两个线程,请问先打印邮件还是短信          邮件
 * 2. sendEmail方法中加入暂停3秒钟,请问先打印邮件还是短信  邮件
 * 3. 添加一个普通的hello方法,请问先打印邮件还是hello     hello
 * 4. 有两部手机,请问先打印邮件还是短信                  短信
 * 5. 有两个静态同步方法,有1部手机,请问先打印邮件还是短信  邮件
 * 6. 有两个静态同步方法,有2部手机,请问先打印邮件还是短信  邮件
 * 7. 有1个静态同步方法,有1个普通同步方法,有1部手机       短信
 * 8. 有1个静态同步方法,有1个普通同步方法,有2部手机       短信
 *
 * 笔记总结:
 * 1-2
 *    一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
 *    其他的线程都只能等待,换句话说,某一个时刻,只能有唯一的一个线程去访问这些synchronized方法
 *    锁的是当前对象this,被锁定后,其他的线程都不能进入到当前对象的其它的synchronized方法
 * 3-4
 *    加个普通方法后发现和同步锁无关
 *    换成两个对象后,不是同一把锁了,情况立刻变化
 *
 * 5-6 都换成静态同步方法后,情况又变化
 *    三种synchronized锁的内容有一些差别:
 *      对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁 -> 实例对象本身
 *      对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板
 *      对于同步方法块,锁的是 synchronized 括号内的对象
 * 7-8
 *    当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁
 *
 *    所有的普通同步方法用的都是同一把锁——实例对象本身,就是new出来的具体实例对象本身,本类this
 *    也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁
 *
 *    所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板Class
 *    具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的
 *    但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁
 */
public class Lock8Demo {

    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            phone.sendEmail();
        }, "a").start();
        //暂停毫秒,保证a线程先启动
        try{ TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
        new Thread(() -> {
            //phone.sendSMS();
            phone.hello();
            //phone2.sendSMS();
        },"b").start();
    }
}

解释说明-小总结:

阿里巴巴规约:【强制】高并发时,同步调用应该去考量锁的性能耗损。能用无锁数据结构,就不要用类锁。
说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用RPC方法

synchronized有三种应用方式

JDK源码(notify方法)说明举例

8种锁的案例实际体现在3个地方:

  1. 作用于实例方法,当前实例加锁,进入同步代码前要获得当前实例的锁
  2. 作用于代码块,对括号里配置的对象加锁
  3. 作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁

从字节码角度分析synchronized实现

  1. javap -c ***.class 文件反编译

    1. -c 对代码进行反汇编
    2. javap -v ***.class文件反编译
    3. -v -verbose 输出附加信息(包括行号、本地变量表,反汇编等详细信息)
  2. synchronized同步代码块:实现使用的是monitorenter和monitorexit指令

    Compiled from "LockSyncDemo.java"
    public class com.juc.locks.LockSyncDemo {
      java.lang.Object object;
    
      public com.juc.locks.LockSyncDemo();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>
    ":()V
           4: aload_0
           5: new           #2                  // class java/lang/Object
           8: dup
           9: invokespecial #1                  // Method java/lang/Object."<init>
    ":()V
          12: putfield      #3                  // Field object:Ljava/lang/Object;
    
          15: return
    
      public void m1();
        Code:
           0: aload_0
           1: getfield      #3                  // Field object:Ljava/lang/Object;
    
           4: dup
           5: astore_1
           6: monitorenter
           7: getstatic     #4                  // Field java/lang/System.out:Ljav
    a/io/PrintStream;
          10: ldc           #5                  // String ----hello synchronized c
    ode block
          12: invokevirtual #6                  // Method java/io/PrintStream.prin
    tln:(Ljava/lang/String;)V
          15: aload_1
          16: monitorexit
          17: goto          25
          20: astore_2
          21: aload_1
          22: monitorexit
          23: aload_2
          24: athrow
          25: return
        Exception table:
           from    to  target type
               7    17    20   any
              20    23    20   any
    
      public static void main(java.lang.String[]);
        Code:
           0: return
    }
    一定是一个enter和两个exit吗?
    一般情况下就是1个enter对应2个exit;极端:m1方法里面自己添加一个异常试试
    
  3. synchronized普通同步方法:调用指令将会检查方法的ACC_SYNCHRONIZED访问标志是否被设置。如果设置了,执行线程会将先持有monitor锁,然后再执行方法,最后在方法完成(无论是正常完成还是非正常完成)时释放monitor

  4. synchronized静态同步方法:ACC_STATIC,ACC_SYNCHRONIZED访问标志区分该方法是否静态同步方法

反编译synchronized锁的是什么

面试题:为什么任何一个对象都可以成为一个锁

什么是管程monitor

大厂面试题讲解

synchronized实现原理,monitor对象什么时候生成的?知道monitor的monitorenter和monitorexit这两个是怎么保证同步的吗,或者说,这两个操作计算机底层是如何执行的

管程

管程(Monitors,也称为监视器)是一种程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件设备或一群变量。对共享变量能够进行的所有操作集中在一个模块中。(把信号量及其操作原语“封装”在一个对象内部)管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。管程提供了一种机制,管程可以看做一个软件模块,它是将共享的变量和对于这些共享变量的操作封装起来,形成一个具有一定接口的功能模块,进程可以调用管程来实现进程级别的并发控制。

通过C底层原语了解

在HotSpot虚拟机中,monitor采用ObjectMonitor实现

上述C++源码解读

​ ObjectMonitor.java -> ObjectMonitor.cpp -> objectMonitor.hpp在这里插入图片描述

​ objectMonitor.hpp
​ 每个对象天生都带着一个对象监视器
​ 每一个被锁住的对象都会和Monitor关联起来

ObjectMonitor中有几个关键属性

_owner指向持有ObjectMonitor对象的线程
_WaitSet存放处于wait状态的线程队列
_EntryList存放处于等待锁block状态的线程队列
_recursions锁的重入次数
_count用来记录该线程获取锁的次数

对于synchronized关键字,我们在《Synchronized与锁升级》章节还会再深入讲解

synchronized必须作用于某个对象中,所以Java对象的头文件存储了的相关信息。锁升级功能主要依赖于MarkWord中的锁标志位和释放偏向锁标志位,后续讲解锁升级时候我们再加深,目前为了承前启后的学习,先对下图混个眼熟即可

Hotspot的实现

在这里插入图片描述

公平锁和非公平锁

从ReentrantLock卖票demo演示公平和非公平现象

class Ticket{//资源类,模拟3个售票员卖完50张票
    private int number = 50;
    ReentrantLock lock = new ReentrantLock(true);

    public void sale(){
        lock.lock();
        try{
            if(number > 0){
                System.out.println(Thread.currentThread().getName()+"卖出第:\t"+(number--)+"\t 还剩下:"+number);
            }
        }finally{
            lock.unlock();
        }
    }
}
/**
 * @author William
 * @create 2022-07-11 20:26
 */
public class SaleTicketDemo {
    public static void main(String[] args) {
        Ticket ticket = new Ticket();

        new Thread(() -> { for(int i = 0; i < 55; i++) ticket.sale();},"a").start();
        new Thread(() -> { for(int i = 0; i < 55; i++) ticket.sale();},"b").start();
        new Thread(() -> { for(int i = 0; i < 55; i++) ticket.sale();},"c").start();
    }
}

何为公平锁 / 非公平锁?

  1. 公平锁:是指多个线程按照申请锁的顺序来获取锁,这里类似排队买票,先来的人先买后来的人在队尾排着,这是公平的 Lock lock = new ReentrantLock(true); //true表示公平锁,先来先得
  2. 非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转或者饥饿的状态(某个线程一直得不到锁) Lock lock = new ReentrantLock(false);//false表示非公平锁,后来的也可能先获得锁,默认非公平

**面试题:为什么会有公平锁/非公平锁的设计?**为什么默认非公平?

  1. 挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分的利用CPU的时间片,尽量减少CPU空闲状态时间
  2. 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当1个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销

什么时候用公平,什么时候用非公平? 如果为了更高的吞吐量·,很显然非公平锁是比较合适的,因为节省很多线程切换时间,吞吐量自然就上去了;否则那就用公平锁,大家公平使用

预埋伏AQS在这里插入图片描述

可重入锁(又名递归锁)

是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。

如果是1个有synchronized修饰的递归调用方法,程序第2次进入被自己阻塞了岂不是天大的笑话,出现了作茧自缚。所以Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可一定程度避免死锁

进入什么:进入同步域(即同步代码块/方法或显式锁锁定的代码)

一句话:一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入。自己可以获取自己的内部锁

可重入锁种类

  1. 隐式锁(即synchronized关键字使用的锁)默认是可重入锁

    指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁。简单的来说就是:在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的

    /**
     * @author William
     * @create 2022-07-11 21:08
    */
    public class ReEntryLockDemo {
    
     public synchronized void m1(){
         //指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁
         System.out.println(Thread.currentThread().getName()+"\t ----come in");
         m2();
         System.out.println(Thread.currentThread().getName()+"\t ----end m1");
     }
     public synchronized void m2(){
         System.out.println(Thread.currentThread().getName()+"\t ----come in");
         m3();
     }
     public synchronized void m3(){
         System.out.println(Thread.currentThread().getName()+"\t ----come in");
     }
    
     public static void main(String[] args) {
         ReEntryLockDemo reEntryLockDemo = new ReEntryLockDemo();
         new Thread(() -> {
             reEntryLockDemo.m1();
         },"t1").start();
     }
     private static void reEntryM1() {
         final Object object = new Object();
         new Thread(() -> {
             synchronized(object){
                 System.out.println(Thread.currentThread().getName()+"\t ----外层调用");
                 synchronized(object) {
                     System.out.println(Thread.currentThread().getName()+"\t ----中层调用");
                     synchronized (object){
                         System.out.println(Thread.currentThread().getName()+"\t ----内层调用");
                     }
                 }
             }
         },"t1").start();
     }
    }
    
  2. Synchronized的重入的实现机理

    每个锁对象拥有一个计数器和一个指向持有该锁的线程的指针。当执行monitorenter时,如果目标锁对象的计数器为零,那么说明它没有被其他线程所持有,Java虚拟机会将该锁对象的持有线程设置为当前线程,并且将其计数器加1

    在目标锁对象的计数器不为零的情况下,如果锁对象的持有线程是当前线程,那么Java虚拟机可以将其计数器加1,否则需要等待,直至持有线程释放该锁

    当执行monitorexit时,Java虚拟机则需将锁对象的计数器减1,计数器为零代表锁已被释放

  3. 显示锁(即Lock)也有ReentrantLock这样的可重入锁

死锁及排查

是什么 产生死锁的主要原因 系统资源不足,进程运行推进的顺序不合适,资源分配不当

死锁是指两个或两个以上的线程在执行过程中,因抢夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁在这里插入图片描述

请写一个死锁代码case

public class DeadLockDemo {
    public static void main(String[] args) {
        final Object objectA = new Object();
        final Object objectB = new Object();

        new Thread(() -> {
            synchronized (objectA){
                System.out.println(Thread.currentThread().getName()+"\t 自己持有A锁,希望获得B锁");
                try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                synchronized(objectB){
                    System.out.println(Thread.currentThread().getName()+"\t 成功获得B锁");
                }
            }
        },"A").start();

        new Thread(() -> {
            synchronized (objectB){
                System.out.println(Thread.currentThread().getName()+"\t 自己持有B锁,希望获得A锁");
                try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                synchronized(objectA){
                    System.out.println(Thread.currentThread().getName()+"\t 成功获得A锁");
                }
            }
        },"B").start();
    }
}

如何排查死锁 jps -l jstack+进程号 图形化 jconsole(通用)

写锁(独占锁)/读锁(共享锁)

自旋锁SpinLock

无锁 -> 独占锁 -> 读写锁 -> 邮戳锁

无锁 -> 偏向锁 -> 轻量锁 -> 重量锁

本章锁内容,上半场小总结

8锁案例运行,锁的到底是什么 对象锁、类锁
公平锁和非公平锁2
可重入锁(又名递归锁)
死锁及排查
为什么任何一个对象都可以成为一个锁 objectMonitor.hpp
小总结(重要):指针指向monitor对象(也称为管程或监视器锁)的起始地。每个对象都存在着一个monitor与之关联,当一个monitor被某个线程持有后,它便处于锁定状态。在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)
在这里插入图片描述

《书到用时方恨少,钱到月底不够花》

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

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

相关文章

五种IO模型以及select多路转接IO模型

目录 一、典型IO模型 1.1 阻塞IO 1.2 非阻塞IO 1.3 信号驱动I0 1.4 IO多路转接 1.5 异步IO 多路转接的作用和意义 二、多路转接IO模型&#xff08;select&#xff09; 2.1 接口 2.2 接口当中的事件集合&#xff1a; fd_set 2.2 select使用事件集合&#xff08;位图&am…

ip公司和soc公司是什么?

IP 公司和 SoC 公司都是半导体行业的重要组成部分&#xff0c;但它们的角色和职责略有不同。IP&#xff08;Intellectual Property&#xff09;公司主要提供可重用的知识产权组件&#xff0c;也称为 IP 核或 IP 模块&#xff0c;这些组件可以在设计芯片的过程中被集成到芯片中。…

Git代码冲突-不同分支之间的代码冲突

1、解决思路在团队开发中&#xff0c;提交代码到Git仓库时经常会遇到代码冲突的问题。- 原因&#xff1a;多人对相同的文件进行了编辑&#xff0c;造成代码存在差异化- 解决方案&#xff1a;1. 使用工具或git命令对比不同分支代码的差异化2. 把不同分支中有效代码进行保留&…

[译文] 基于PostGIS3.1 生成格网数据

根据格网进行数据统计与分析是一种常用的方法&#xff0c;相比自然地理边界与行政管理边界而言&#xff0c;使用格网有如下特点&#xff1a;每个格网之间地位相等&#xff0c;没有上下级之分。每个格网的面积都相等。相邻两个格网单元中心点之间距离相等。适用于将数据从“空间…

ThreeJS加载公路GeoJson数据实现流光效果

threejs加载公路geojson数据,跟加载行政区域的原理一样,唯一不同的是geojson格式不一样,路线并不是连贯起来的,按照路段进行的拆分,在加载的时候问题不大,正常解析然后转墨卡托投影,但是在做流光效果时,需要对geojson进行重新组合. 实现效果:

Android:反编译apk踩坑/apktool/dex2jar/JDGUI

需求描述 想要反编译apk文件&#xff0c;搜到了这篇博客&#xff1a;Android APK反编译就这么简单 详解&#xff08;附图&#xff09;&#xff0c;非常有参考价值~但其中的工具下载链接都已404&#xff0c;而本杂鱼实际操作的过程中也出现了亿点点点点点点的问题&#xff0c;于…

电子技术——反馈对放大器极点的影响

电子技术——反馈对放大器极点的影响 放大器的频率响应和稳定性可以直接由其极点决定。因此我们将深入反馈对放大器极点的影响。 稳定性和极点位置 我们首先讨论稳定性和极点位置的关系。首先我们给出结论&#xff0c;对于任何一个稳定的放大器&#xff0c;其极点都处在 sss …

prometheus + alterManager + 飞书通知,实现服务宕机监控告警;实测可用

架构设计图 最终效果图 项目准备 xml依赖 <!-- 监控相关 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>io.…

Elasticsearch7.8.0版本进阶——段合并

目录一、段的概述1.1、段的概念1.2、段的缺点1.3、如何解决段数量暴增问题二、段合并的流程三、段合并的注意事项一、段的概述 1.1、段的概念 每一 段 本身都是一个倒排索引。 1.2、段的缺点 由于自动刷新流程每秒会创建一个新的段 &#xff0c;这样会导致短时间内的段数量…

interrupt多线程设计模式

1. 两阶段终止-interrupt Two Phase Termination 在一个线程T1中如何“优雅”终止线程T2&#xff1f;这里的【优雅】指的是给T2一个料理后事的机会。 错误思路 ● 使用线程对象的stop()方法停止线程&#xff08;强制杀死&#xff09; —— stop&#xff08;&#xff09;方法…

Linux内核的虚拟内存(MMU、页表结构)

前言&#xff1a;内存是程序得以运行的重要物质基础。如何在有限的内存空间运行较大的应用程序&#xff0c;曾是困扰人们的一个难题。为解决这个问题&#xff0c;人们设计了许多的方案&#xff0c;其中最成功的当属虚拟内存技术。Linux作为一个以通用为目的的现代大型操作系统&…

【git】Idea中git的使用

配置git 创建git仓库 不同颜色代表的含义 红色——未加入版本控制&#xff1b;绿色——已经加入控制暂未提交&#xff1b;蓝色——加入&#xff0c;已提交&#xff0c;有改动&#xff1b;白色——加入&#xff0c;已提交&#xff0c;无改动&#xff1b;灰色——版本控制已忽略文…

8、STM32 FSMC驱动LCD(ILI93xx)

本文使用FSMC驱动LCD显示&#xff0c;关于建议先看之前的7、STM32 FSMC驱动SRAM一文 硬件连接&#xff1a; 一、CubeMx配置FSMC驱动LCD ILI93xx 此章只为快速使用LCD&#xff0c;不涉及原理、指令说明 显示屏驱动文件参考正点探索者 1、CubeMx图形配置 此处的时序还可以调…

GLOG如何清理日志

1 日志清理 其实GLOG很长时间以来都没有日志清理功能。小白对此也很震惊&#xff0c;还特意去查了GLOG的提交记录。代码的提交记录显示&#xff0c;GLOG与日志清理有关的最初代码是2019年11月1日&#xff0c;而这个开源项目的起始时间可以追溯到2008年。也就是说&#xff0c;在…

浅谈liunx init.d 和 rc.local 两种起动方式

浅谈liunx init.d 和 rc.local 两种起动方式 以rabbitmq 举例 &#xff08;一&#xff09;.init.d 方式 开机自动重启设置 1.在/etc/init.d 目录下新建一个 rabbitmq [rootlocalhost init.d]# vi rabbitmq具体脚本如下所示&#xff1a; #!/bin/bash # # chkconfig: 2345 …

【离线数仓-7-数据仓库开发DIM层设计要点-拉链表同步装载脚本】

离线数仓-7-数据仓库开发DIM层设计要点-拉链表同步&装载脚本离线数仓-7-数据仓库开发DIM层设计要点-拉链表同步&装载脚本一、DIM层 维度模型 设计要点6.用户维度表 -拉链表1.用户维度表 前期梳理2.用户维度表 DDL表设计分析3.用户维度表 加载数据分析1.拉链表首日装载数…

RocketMQ 5.x新版本部署优化一览

​ RocketMQ从2022年9月份开始推出了新的5.x大版本。相比于之前的4.x版本&#xff0c;5.x版本向云原生前进了一大步。在增强原因功能的基础上&#xff0c;更是支持多语言客户端&#xff0c;周边生态也进行了补强和完善&#xff0c;明显可以看到离Kafka老大哥又近了很大一步。 …

linux网络编程-多进程实现TCP并发服务器

服务端流程步骤socket函数创建监听套接字lfdbind函数将监听套接字绑定ip和端口listen函数设置服务器为被动监听状态&#xff0c;同时创建一条未完成连接队列&#xff08;没走完tcp三次握手流程的连接&#xff09;&#xff0c;和一条已完成连接队列&#xff08;已完成tcp三次握手…

3-虚拟机篇

一.java JVM 的内存结构 内存&#xff1a;按线程类型分两类 线程共享&#xff1a; 方法区&#xff1a;存放类的信息堆&#xff1a;存放java对象的信息 线程私有&#xff1a; java虚拟机栈&#xff1a;存放java方法、方法参数和局部变量程序计数器&#xff1a;记录程序执行…

mars3d将当前视⻆指向北⽅且加载建筑物白膜

通过 setView⽅法实现&#xff0c;可以设置heading参数控制相对旋转⻆度map.scene.camera.setView({ orientation: { heading: 0, } }) 相关示例&#xff1a;1.http://mars3d.cn/editor-vue.html?idmap/options/scene2.功能示例(Vue版) | Mars3D三维可视化平台 | 火星科技// *…