并发编程之死锁问题介绍

news2024/11/27 22:41:53

一、本文概览

死锁问题在并发编程中是一个非常致命的问题,问题一旦产生,只能通过重启机器、修改代码来修复问题,下面我们通过一小段文章内容介绍下死锁以及如何死锁的预防

在这里插入图片描述

二、什么是死锁?

在介绍死锁之前,先来明确下什么是锁?

  • 锁其实就是一种同步机制,一个线程拥有对一块资源的锁,那么该线程对这块资源的处理是与其他线程互斥的!在该线程未释放锁之前,其它线程会被限制对统一资源的访问。

有了对锁概念的认知,我们再来看下死锁是什么?所谓死锁其实就是指在多线程或者多进程运行状态下,因争夺资源而导致的一种互不让步的僵局,导致各自均进入阻塞状态,如果没有外力(强制中断或者程序设置超时中断)的推动,则程序将一直处于僵持(崩溃)状态。

三、死锁产生的必要条件?

产生死锁有四个必要条件:

  • 资源互斥:资源互斥就是上面我们说的锁,线程持有的锁是需要具备独占且排他使用的
  • 不可被剥夺:线程在对持有的资源未使用完毕前,是不会被其他线程强行剥夺的
  • 请求并保持:线程在请求获取新的资源时,当前所持有的资源依旧继续占用
  • 循环等待:多个线程对于获取锁行为是一个环形,例如线程A持有锁1,需要获取锁2才能进行后续操作;线程B持有锁2,需要获取锁1才能进行后续操作,此时就形成了环

我们来看看死锁的代码示例:

package com.markus.onjava.concurrent;

/**
 * @author: markus
 * @date: 2023/2/22 10:21 PM
 * @Description: 死锁代码演示
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";
        Thread thread1 = new Thread(new Task(lockA, lockB));
        Thread thread2 = new Thread(new Task(lockB, lockA));
        thread1.start();
        thread2.start();
    }
}

class Task implements Runnable {

    private final String lockA;
    private final String lockB;

    public Task(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        try {
            synchronized (lockA) {
                log("contain lock:[" + lockA + "]");
                Thread.sleep(2000);
                synchronized (lockB) {
                    log("contain lock:[" + lockB + "]");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void log(String msg) {
        System.out.println("Thread id [" + Thread.currentThread().getId() + "] msg: " + msg);
    }
}

控制台

在这里插入图片描述

我们可以通过jstack来查看进程中的线程状态来分析是哪些线程出现了死锁状态

# 查询进程id
➜  ~ jps
52996 Launcher
53734 Jps
53498 Launcher
53499 DeadLockDemo
5452
52862 RemoteMavenServer36
# 分析进程的堆栈信息
➜  ~ jstack 53499
2023-02-22 22:58:31
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.211-b12 mixed mode):

"Thread-1" #12 prio=5 os_prio=31 tid=0x00007f9e2d84d000 nid=0x5b03 waiting for monitor entry [0x000000030a74d000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.markus.onjava.concurrent.Task.run(DeadLockDemo.java:38)
	- waiting to lock <0x000000076ac1c8d0> (a java.lang.String)
	- locked <0x000000076ac1c908> (a java.lang.String)
	at java.lang.Thread.run(Thread.java:748)

"Thread-0" #11 prio=5 os_prio=31 tid=0x00007f9e2d84c800 nid=0x9c5f waiting for monitor entry [0x000000030a64a000]
   java.lang.Thread.State: BLOCKED (on object monitor)
	at com.markus.onjava.concurrent.Task.run(DeadLockDemo.java:38)
	- waiting to lock <0x000000076ac1c908> (a java.lang.String)
	- locked <0x000000076ac1c8d0> (a java.lang.String)
	at java.lang.Thread.run(Thread.java:748)

# ... 省略部分线程堆栈信息
Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x00007f9e2f814168 (object 0x000000076ac1c8d0, a java.lang.String),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00007f9e2f8169f8 (object 0x000000076ac1c908, a java.lang.String),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
	at com.markus.onjava.concurrent.Task.run(DeadLockDemo.java:38)
	- waiting to lock <0x000000076ac1c8d0> (a java.lang.String)
	- locked <0x000000076ac1c908> (a java.lang.String)
	at java.lang.Thread.run(Thread.java:748)
"Thread-0":
	at com.markus.onjava.concurrent.Task.run(DeadLockDemo.java:38)
	- waiting to lock <0x000000076ac1c908> (a java.lang.String)
	- locked <0x000000076ac1c8d0> (a java.lang.String)
	at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

四、如何防范死锁?

上面介绍了产生死锁的必要条件,所谓必要条件就是要都满足才能产生死锁,所以在预防的时候,我们可以任意打破死锁的4个必要条件来预防死锁,但又因为资源互斥是底层操作系统的固有特性,应用层面是无法改变的,所以我们可以通过破坏剩余的三个条件来进行预防:

  • 破坏"不可被剥夺"条件
  • 破坏"请求并保持"条件
  • 破坏"循环等待"条件

下面来举个经典的死锁案例:哲学家晚餐情景。

一个餐桌上有五名哲学家以及五根筷子,这五根筷子分别插入到五位哲学家的间隔处,哲学家想要饮食则必须同时拿起左右手两边的筷子,所以现在就有问题了,如果有人能够吃饭,那肯定会有人等待,在某种情况下还极可能造成所有人都在等待以导致死锁的情况。

在这里插入图片描述

我们来用代码实现一下这种场景:

  • 筷子持有
package com.markus.onjava.concurrent.deadlock;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * @author: markus
 * @date: 2023/2/25 10:12 PM
 * @Description: 筷子持有者
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class StickHolder {
    private static class Chopstick {
    }

    private Chopstick stick = new Chopstick();

    // 利用阻塞队列实现当前筷子同一时刻只能被一个人持有,其他人想要获取这跟筷子,则必须等待
    private BlockingQueue<Chopstick> holder = new ArrayBlockingQueue<>(1);

    public StickHolder() {
        putDown();
    }

    /**
     * 拿起筷子
     */
    public void pickUp() {
        try {
            holder.take(); // 不可用时会阻塞
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 放下筷子
     */
    public void putDown() {
        try {
            holder.put(stick);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
  • 哲学家
package com.markus.onjava.concurrent.deadlock;

import java.util.concurrent.TimeUnit;

/**
 * @author: markus
 * @date: 2023/2/25 10:19 PM
 * @Description: 哲学家
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class Philosopher implements Runnable {
    private final int seat;
    private final StickHolder left, right;

    public Philosopher(int seat, StickHolder left, StickHolder right) {
        this.seat = seat;
        this.left = left;
        this.right = right;
    }

    @Override
    public String toString() {
        return "P" + seat;
    }

    @Override
    public void run() {
      	// 循环执行吃饭的动作
        while (true) {
            // 拿起右手边的筷子
            right.pickUp();
            // 拿起左手边的筷子
            left.pickUp();
            System.out.println(this + " eating");
            // 放下右手边的筷子
            right.putDown();
            // 放下左手边的筷子
            left.putDown();
        }
    }
}

  • 晚餐现场
package com.markus.onjava.concurrent.deadlock;

import com.markus.onjava.Nap;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;

/**
 * @author: markus
 * @date: 2023/2/25 10:23 PM
 * @Description: 哲学家的晚餐演示
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class DiningPhilosophers {
    /*筷子持有者*/
    private StickHolder[] sticks;
    /*哲学家*/
    private Philosopher[] philosophers;

    public DiningPhilosophers(int n) {
        sticks = new StickHolder[n];
        Arrays.setAll(sticks, i -> new StickHolder());
        philosophers = new Philosopher[n];
	      // 通过模数n选择右手边的筷子,并将最后一个哲学家指向第一位哲学家旁边,整体形成一个环
        Arrays.setAll(philosophers, i -> new Philosopher(i, sticks[i], sticks[(i + 1) % n])); 
        Arrays.stream(philosophers)
                .forEach(CompletableFuture::runAsync);

    }

    public static void main(String[] args) {
      	// 立刻返回,不阻塞
        new DiningPhilosophers(5);
      	// 主流程等待100s后再退出(此时我们可以观察哲学家的用餐场景)
        new Nap(100, "Shutdown");
    }
}

执行晚餐现场的代码后,我们可以发现,起初还能正常的执行,但一会就会形成互相等待的场景,这种等待形成了一个环,也就造成了死锁。

在这里插入图片描述

我们也可以通过jstack命令来确认下互相等待的情况:

➜  ~ jps
1593
10331 Jps
10318 Launcher
8862 JStack
10319 DiningPhilosophers
➜  ~ jstack 10319
2023-02-26 14:51:08
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.211-b12 mixed mode):

"ForkJoinPool.commonPool-worker-5" #17 daemon prio=5 os_prio=31 tid=0x00007fb25188f800 nid=0x6a07 waiting on condition [0x000000030668e000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076b249450> (a java.util.concurrent.ForkJoinPool)
	at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

"ForkJoinPool.commonPool-worker-7" #19 daemon prio=5 os_prio=31 tid=0x00007fb211809000 nid=0x9503 waiting on condition [0x000000030658b000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076b249450> (a java.util.concurrent.ForkJoinPool)
	at java.util.concurrent.ForkJoinPool.awaitWork(ForkJoinPool.java:1824)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1693)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

"ForkJoinPool.commonPool-worker-6" #18 daemon prio=5 os_prio=31 tid=0x00007fb211808800 nid=0x6703 waiting on condition [0x0000000306488000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076b18a040> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
	at com.markus.onjava.concurrent.deadlock.StickHolder.pickUp(StickHolder.java:30)
	at com.markus.onjava.concurrent.deadlock.Philosopher.run(Philosopher.java:31)
	at java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1626)
	at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java)
	at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1618)
	at java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

"ForkJoinPool.commonPool-worker-4" #16 daemon prio=5 os_prio=31 tid=0x00007fb2540cd000 nid=0x9703 waiting on condition [0x0000000306385000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076b189f70> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
	at com.markus.onjava.concurrent.deadlock.StickHolder.pickUp(StickHolder.java:30)
	at com.markus.onjava.concurrent.deadlock.Philosopher.run(Philosopher.java:31)
	at java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1626)
	at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java)
	at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1618)
	at java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

"ForkJoinPool.commonPool-worker-3" #15 daemon prio=5 os_prio=31 tid=0x00007fb253849000 nid=0x9903 waiting on condition [0x0000000306282000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076b189ea0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
	at com.markus.onjava.concurrent.deadlock.StickHolder.pickUp(StickHolder.java:30)
	at com.markus.onjava.concurrent.deadlock.Philosopher.run(Philosopher.java:31)
	at java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1626)
	at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java)
	at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1618)
	at java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

"ForkJoinPool.commonPool-worker-2" #14 daemon prio=5 os_prio=31 tid=0x00007fb252030800 nid=0x6303 waiting on condition [0x000000030617f000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076b189dd0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
	at com.markus.onjava.concurrent.deadlock.StickHolder.pickUp(StickHolder.java:30)
	at com.markus.onjava.concurrent.deadlock.Philosopher.run(Philosopher.java:31)
	at java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1626)
	at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java)
	at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1618)
	at java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

"ForkJoinPool.commonPool-worker-1" #13 daemon prio=5 os_prio=31 tid=0x00007fb25383a000 nid=0x6133 waiting on condition [0x000000030607c000]
   java.lang.Thread.State: WAITING (parking)
	at sun.misc.Unsafe.park(Native Method)
	- parking to wait for  <0x000000076b189d00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
	at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
	at com.markus.onjava.concurrent.deadlock.StickHolder.pickUp(StickHolder.java:30)
	at com.markus.onjava.concurrent.deadlock.Philosopher.run(Philosopher.java:31)
	at java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1626)
	at java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java)
	at java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1618)
	at java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:289)
	at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java)
	at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
	at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
	at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)


通过对上面基础知识的学习,我们可以知道代码产生死锁的原因有以下几点:

  • 一根筷子(资源)同一时刻只能被一位哲学家使用,也就是资源互斥
  • 哲学家每人都持有一根筷子,并且均尝试去获取另一根筷子,就是请求并保持
  • 哲学家持有的筷子不能被强制回收,只能由自己完成任务主动释放,也就是不可被剥夺
  • 哲学家的等待形成了一个环,造成循环等待

前面我们说了资源互斥是系统特性,我们先忽略打破这个条件,下面我们来通过其他三个条件来实现预防死锁的解决方案:

  • 打破循环等待-我们可以看到,哲学家都是尝试先拿起右边的筷子再拿起右边的筷子,这样就互相之间就形成了环,我们可以指定某一位哲学家不按照这样的顺序取筷子,则形成不了环,也就不会造成情况了。
package com.markus.onjava.concurrent.deadlock;

import com.markus.onjava.Nap;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;

/**
 * @author: markus
 * @date: 2023/2/25 10:23 PM
 * @Description: 哲学家的晚餐演示
 * @Blog: https://markuszhang.com
 * It's my honor to share what I've learned with you!
 */
public class DiningPhilosophers {
    /*筷子持有者*/
    private StickHolder[] sticks;
    /*哲学家*/
    private Philosopher[] philosophers;

    public DiningPhilosophers(int n) {
        sticks = new StickHolder[n];
        Arrays.setAll(sticks, i -> new StickHolder());
        philosophers = new Philosopher[n];
        Arrays.setAll(philosophers, i -> new Philosopher(i, sticks[i], sticks[(i + 1) % n]));
        philosophers[1] = new Philosopher(1, sticks[0], sticks[1]); // [1] 通过将第2位哲学家颠倒拿放筷子的顺序来修正死锁
        Arrays.stream(philosophers)
                .forEach(CompletableFuture::runAsync);

    }

    public static void main(String[] args) {
        new DiningPhilosophers(5);
        new Nap(100, "Shutdown");
    }
}
  • 打破循环条件还有另一种方案,就是调大资源数超出你的机器的CPU物理核数,这样就导致同一时刻,肯定会有一位哲学家不参与活动的进行,这也就形成不了循环等待的情况
  • 我们还可以通过打破请求并保持的条件
/**
 * 拿起筷子
 */
public void pickUp() {
    try {
//      holder.take(); // 不可用时会阻塞
        holder.poll(100, TimeUnit.MILLISECONDS);// 超时自己中断,不再尝试去获取锁
    } catch (InterruptedException e) {
//      throw new RuntimeException(e);
    }
}
  • 不可被剥夺条件实现起来比较麻烦,核心思想就是如果拿不到就去旁边手中抢过来。

综上,预防死锁的方式可以通过打破那四个必要条件的其中一个即可,其中打破循环等待条件又是最容易的,我们通常会通过这种思路进行预防。

五、本文总结

上述就是对死锁的简单介绍,包括死锁是什么,什么情况下会导致死锁(四个必要条件)以及如何预防死锁(打破四个必要条件之一即可),我们也可以看出在Java语言层面上是无法支持我们来避免死锁的,所以我们只能通过谨慎的设计来避免这个问题。避免并发问题,最简单有效的方法就是永远不要共享资源,但这看来也是不现实的,所以在设计的时候,我们也可以通过画布画出资源的依赖关系来谨慎设计方案等等其他方法来避免系统出现死锁。

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

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

相关文章

对象分配策略

对象创建后,究竟何去何从,对象在堆中又会经历哪些过程,本篇就会详细解释对象创建后直到对象被回收的整个过程。之前博主已经写过Minor GC、Major GC、Full GC的区别&#xff0c;而本篇也主要根据这几个GC开展。 对象回收过程流程如下图所示&#xff1a; 正常的对象生存过程&a…

NetSuite Saved Search中When Ordered By Field 与 Keep Dense_Rank辨析

今朝的题目是一个隐藏的宝藏话题&#xff0c;Saved Search中我们极少用的一个功能--When Ordered By Field和Keep Dense_Rank。 假如你碰到一个需求&#xff0c;要求是&#xff1a;“在销售历史中按照客户别&#xff0c;取最早交易日期的销售金额&#xff0c;以识别VIP客户”。…

QML Animation动画详解

1.Animation简介 Animation类型提供了四个属性&#xff1a; alwaysRunToEnd&#xff1a;该属性接收布尔类型的参数。该属性保存动画是否运行到完成才停止。当loops属性被设置时&#xff0c;这个属性是最有用的&#xff0c;因为动画将正常播放结束&#xff0c;但不会重新启动。…

PMP值得考吗?

第一&#xff0c;PMP的价值体现 1、PMP是管理岗位必考证书。 多数企业会选择优先录用持PMP证书的管理人才&#xff0c;PMP成为管理岗位的必考证书。PMP在很多外企和国内中大型企业非常受重视&#xff0c;中石油、中海油、华为等等都会给内部员工做培训。 这些机构对项目管理…

超简单 华为OD机试用Python实现 -【无向图染色问题 or 红黑图】(2023-Q1 新题)

华为OD机试题 华为OD机试300题大纲无向图染色问题 or 红黑图题目描述输入描述输出描述说明示例一输入输出示例二输入输出Python 代码实现华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog…

Js高级API

Decorator装饰器 针对属性 / 方法的装饰器 // decorator 外部可以包装一个函数&#xff0c;函数可以带参数function Decorator (type) {/*** 这里是真正的decorator* description: 装饰的对象的描述对象* target:装饰的属性所述类的原型&#xff0c;不是实例后的类。如果装饰…

Python每日一练(20230226)

1. 合并列表中字典字段 如下两个列表&#xff0c;需要将oldList转化为newList&#xff0c;去掉相同字段的字典&#xff0c;并且去掉的参数里面的值要相加。 oldList [{0-0: 0, 0-1: 0, 0-2: 0, 0-3: 1972}, {3-3: 203, 3-2: 0, 3-1: 0, 3-0: 0}, {0-0: 0, 0-1: 0, 0-2: 0, 0…

Git ---- IDEA集成 GitHub

Git ---- IDEA集成 GitHub1. 设置 GitHub 账号2. 分享工程到 GitHub3. push 推送本地库到远程库4. pull 拉取远程库到本地库5. clone 克隆远程库到本地1. 设置 GitHub 账号 新版的 IDEA 选择之后会自动登录&#xff0c;就不需要设置 token 了。 如果是老版的 IDEA 的话&…

随想录二刷Day06——链表

文章目录链表6. 删除链表的倒数第 N 个结点7. 链表相交8. 环形链表 II链表 6. 删除链表的倒数第 N 个结点 19. 删除链表的倒数第 N 个结点 思路&#xff1a; 用双指针的方法&#xff0c;fast 和 slow 之间保持距离为 n&#xff0c;只需要遍历一次即可完成删除任务。 为了方便…

操作系统笔记-第一章

文章目录操作系统概述1. 操作系统的概念1.1 操作系统的地位1.2 操作系统的作用1.3 操作系统的定义2. 操作系统的历史2.1 操作系统的产生2.1.1 手动操作阶段&#xff08;20世纪40年代&#xff09;2.1.2 批处理阶段&#xff08;20世纪50年代&#xff09;2.1.3 执行系统阶段&#…

aws console 使用fargate部署aws服务快速跳转前端搜索栏

测试过程中需要在大量资源之间跳转&#xff0c;频繁的点击不如直接搜索来的快&#xff0c;于是写了一个搜索框方便跳转。 前端的静态页面可以通过s3静态网站托管实现&#xff0c;但是由于中国区需要备案的原因&#xff0c;可以使用ecs fargate部署 步骤如下&#xff1a; 编写…

Springboot打包成jar发布

打包的方式 打包成jar包打包成war包 区别&#xff1a;jar包内置了tomcat、netty等服务器&#xff0c;更改只需要修改pom.xml的坐标即可&#xff0c;war不内置服务器&#xff0c;需要上传到服务器tomcat解压后运行 如何打包&#xff1f; 打包成jar&#xff0c;pom.xml中设置打…

Linux基础命令-df显示磁盘的使用情况

文章目录 文章目录 df 命令介绍 语法格式 基本参数 参考实例 1&#xff09;以人类可读形式显示磁盘空间的使用情况 2&#xff09;显示磁盘的inode信息 3&#xff09;显示磁盘和文件系统类型 4&#xff09;指定显示文件系统 5&#xff09;显示所有磁盘空间中的内容 …

C++11多线程编程 三:锁资源管理和条件变量

C11多线程编程 一&#xff1a;多线程概述 C11多线程编程 二&#xff1a;多线程通信&#xff0c;同步&#xff0c;锁 C11多线程编程 三&#xff1a;锁资源管理和条件变量 3.1 手动实现RAii管理mutex资源锁自动释放 自己写的代码一般都是自己上锁&#xff0c;自己进行释放&…

【fly-iot飞翔物联】(2):如何从0打造自己的物联网平台,使用开源的技术栈搭建一个高性能的物联网平台,目前在设计阶段。

目录前言1&#xff0c;fly-iot飞翔物联2&#xff0c;mqtt-broker 服务3, 管理后台产品/设备设计4,数据存储目前使用mysql&#xff0c;消息存储到influxdb中5,规则引擎使用 ekuiper6, 总结和其他的想法前言 本文的原文连接是: https://blog.csdn.net/freewebsys/article/details…

3-2 创建一个至少有两个PV组成的大小为20G的名为testvg的VG

文章目录1. 在vmware添加多块20G的硬盘&#xff0c;并创建分区2. 创建一个至少有两个PV组成的大小为20G的名为testvg的VG&#xff0c;要求PE大小为16M&#xff0c;而后在卷组中创建大小为5G的逻辑卷testlv;挂载至/users目录3. 新建用户archlinux,要求其家目录为/users/archlinu…

华为OD机试题,用 Java 解【字符串变换最小字符串】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

合规要点解析:如何正确的使用隐私增强技术

开放隐私计算开放隐私计算开放隐私计算OpenMPC是国内第一个且影响力最大的隐私计算开放社区。社区秉承开放共享的精神&#xff0c;专注于隐私计算行业的研究与布道。社区致力于隐私计算技术的传播&#xff0c;愿成为中国 “隐私计算最后一公里的服务区”。183篇原创内容公众号G…

名创优品第二季度财报预测:收入和利润都将大幅下降

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 名创优品2023财年第二季度财务业绩预测 名创优品(MNSO) 将于2月28日公布2023会计年度第二季度(2022年9月30日至12月31日)财务业绩。 卖方分析师认为&#xff0c;名创优品2023财年第二季度的财务业绩将不如2023财年第一季度。…

前端常考react面试题(持续更新中)

react diff 算法 我们知道React会维护两个虚拟DOM&#xff0c;那么是如何来比较&#xff0c;如何来判断&#xff0c;做出最优的解呢&#xff1f;这就用到了diff算法 diff算法的作用 计算出Virtual DOM中真正变化的部分&#xff0c;并只针对该部分进行原生DOM操作&#xff0c;而…