2023春招面试题:Java并发相关知识

news2024/11/23 21:26:35

1.基础知识回顾

1.1 什么是多线程?

在没有线程的年代,在同一个进程中,程序的处理流程都是顺序的,下一个流程的开始必须等待上

一个流程的结束,如果其中某一个流程非常耗时,那么会影响整个流程的处理时间

cpu执行过程中并不是一个程序执行完之后 cpu 才切换 ,cpu 时间片用完, 就会切换到下个线程执

行,给人一种多程序同时执行的感觉

有了进程以后,为什么还要发明线程呢?

1. 在多核CPU中,利用多线程可以实现并行 执行

2. 同步处理的流程容易发生阻塞,可以用线程来实现异步处理,提高程序处理实时性

3. 线程可以认为是轻量级的进程,所以线程的创建、销毁 比进程更快 (性能开销更小)

1.2.线程解决了什么问题?

单位时间内处理复杂且庞大的数据或业务时提升效率

1)如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创

建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

2)如果大量线程在执行,会涉及到线程间上下文的切换,会极大的消耗CPU运算资源

1.3 如何创建线程?

1) 继承Thread

2)实现Runnable接口

3) 使用 Callable接口 (可以使用CompletableFuture )

注意:

我们项目中使用多线程编程一定要使用线程池,否则可能会导致线程创建过多发生异常

1.4 线程安全

多个线程在对共享数据进行读改写的时候,可能导致的数据错乱就是线程的安全问题了

如何判断当前程序中是否存在线程安全问题?

1. 是否存在多线程环境

2. 在多线程环境下是否存在共享变量

3. 在多线程环境下是否存在对共享变量 “写” 操作

2.线程的生命周期?线程有几种状态

1.线程通常有五种状态,创建,就绪,运行、阻塞和死亡状态。

  1. 新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start方法。该状态的线程位于

可运行线程池中,变得可运行,等待获取CPU的使用权。

3 .运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。

  1. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进

入就绪状态,才有机会转到运行状态。

  1. 死亡状态(Dead):线程执行完了或者因异常退出了run方法,该线程结束生命周期。

2.阻塞的情况又分为三种:

(1)、等待阻塞:运行的线程执行wait方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待

池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify或notifyAll方法才能被唤

醒,wait是object类的方法

(2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放

入“锁池”中。

(3)、其他阻塞:运行的线程执行sleep或join方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状

态。当sleep状态超时、join等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

sleep是Thread类的方法

3. wait 和 sleep 的区别

共同点

  • wait() ,wait(long) 和 sleep(long) 的效果都是让当前线程暂时放弃 CPU 的使用权,进入阻塞状态

不同点

  • 方法归属不同
    • sleep(long) 是 Thread 的静态方法
    • 而 wait(),wait(long) 都是 Object 的成员方法,每个对象都有
  • 醒来时机不同
    • 执行 sleep(long) 和 wait(long) 的线程都会在等待相应毫秒后醒来
    • wait(long) 和 wait() 还可以被 notify 唤醒,wait() 如果不唤醒就一直等下去
    • 它们都可以被打断唤醒
  • 锁特性不同(重点)
    • wait 方法的调用必须先获取 wait 对象的锁,而 sleep 则无此限制
    • wait 方法执行后会释放对象锁,允许其它线程获得该对象锁(我放弃 cpu,但你们还可以用)
    • 而 sleep 如果在 synchronized 代码块中执行,并不会释放对象锁(我放弃 cpu,你们也用不了)

4. volatile的作用和原理

4.1 JMM内存模型

JMM让java程序与硬件指令进行了隔离

由于JVM运行程序的实体是线程,创建每个线程时,java 内存模型会为其创建一个工作内存(我们一般称为栈),工作内存是每个线程的私有数据区域。

Java内存模型规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问。

但线程对变量的操作(读取、赋值等)必须在工作内存中进行。因此首先要将变量从主内存拷贝到自己的工作内存,然后对变量进行操作,操作完成后再将变量写会主内存中。


4.2 Java并发编程要解决的三个问题(三大特征)

原子性

一个线程在CPU中操作不可暂定,也不可中断,要不执行完成,要不不执行

内存可见性

默认情况下变量,当一个线程修改内存中某个变量时,主内存值发生了变化,并不会主动通知其他线程,即其他线程并不可见

有序性

程序执行的顺序按照代码的先后顺序执行。

4.3 Volatile

volatile帮我们解决了:

内存可见性问题

指令重排序问题

不能保证变量操作的原子性(Atomic)

1. 被volatile修饰的共享变量对所有线程总是可见的,也就是当一个线程修改了一个被volatile修

饰共享变量的值,新值总是可以被其他线程立即得知。(会主动通知)

我们可以通过如下案例验证

import java.util.Date;

public class MyData {

    private boolean flag=false;
    public void setFlag(boolean flag) {
        this.flag = flag;
    }
    public boolean isFlag() {
        return flag;
    }

    public static void main(String[] args) throws Exception {
        MyData myData = new MyData();
        // 线程1 修改值
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 子线程3s 后 修改为true
                myData.setFlag(true);
            }
        }).start();

        System.out.println(new Date());
        while (!myData.isFlag()){
            // (如果不用 volatile)  理论上 3s  这里的死循环会结束,但是 实际上3s 后主线程一直在死循环
            // 如果不用 volatile 主线程并没有感知到 子线程修改了变量
        }
        System.out.println("已经被修改了"+new Date());
    }
}

注意: volatile 并不保证线程安全的,即多个线程同时操作某个变量依旧会出现线程安全问题

如下案例

public class MyData {

       private volatile int number=1;
       public void addNum(){
           number++;
       }

    public static void main(String[] args) {
        MyData myData = new MyData();

        // 启动20个线程,每个线程将myData的number值加1000次,那么理论上number值最终是20000
        for (int i=0; i<20; i++) {
            new Thread(() -> {
                for (int j=0; j<1000; j++) {
                    myData.addNum();
                }
            }).start();
        }

        // 程序运行时,有主线程和垃圾回收线程也在运行。如果超过2个线程在运行,那就说明上面的20个线程还有没执行完的,就需要等待
     
        while (Thread.activeCount()>2){
            Thread.currentThread().getThreadGroup().activeCount();
            Thread.yield();// 交出CPU 执行权
        }
        System.out.println("number值加了20000次,此时number的实际值是:" + myData.number);
    }

}

2. 禁止指令重排序优化。

int a = 0; 
bool flag = false; 
public void write() { 
    a = 2; //1 
    flag = true; //2 
}
public void multiply() { 
        if (flag) { //3 
        int ret = a * a;//4 
    } 
} 

write方法里的1和2做了重排序,线程1先对flag赋值为true,随后执行到线程2,ret直接计算出结果,

再到线程1,这时候a才赋值为2,很明显迟了一步。

但是用 flag 使用 volatile修饰之后就变得不一样了

使用volatile关键字修饰后,底层执行时会禁止指令重新排序,按照顺序指令

5.为什么用线程池?解释下线程池参数?

1、降低资源消耗;提高线程利用率,降低创建和销毁线程的消耗。

2、提高响应速度;任务来了,直接有线程可用可执行,而不是先创建线程,再执行。

3、提高线程的可管理性;线程是稀缺资源,使用线程池可以统一分配调优监控。

/*
corePoolSize 代表核心线程数,也就是正常情况下创建工作的线程数,这些线程创建后并不会 
消除,而是一种常驻线程 

maxinumPoolSize 代表的是最大线程数,它与核心线程数相对应,表示最大允许被创建的线程 
数,比如当前任务较多,将核心线程数都用完了,还无法满足需求时,此时就会创建新的线程,但 
是线程池内线程总数不会超过最大线程数 

keepAliveTime 、 unit 表示超出核心线程数之外的线程的空闲存活时间,也就是核心线程不会 
消除,但是超出核心线程数的部分线程如果空闲一定的时间则会被消除,我们可以通过keepAliveTime 、
unit 表示超出核心线程数之外的线程的空闲存活时间,
也就是核心线程不会 setKeepAliveTime 来设置空闲时间 


workQueue 用来存放待执行的任务,假设我们现在核心线程都已被使用,还有任务进来则全部放 
入队列,直到整个队列被放满但任务还再持续进入则会开始创建新的线程 

ThreadFactory 实际上是一个线程工厂,用来生产线程执行任务。我们可以选择使用默认的创建 
工厂,产生的线程都在同一个组内,拥有相同的优先级,且都不是守护线程。当然我们也可以选择 
自定义线程工厂,一般我们会根据业务来制定不同的线程工厂 

Handler 任务拒绝策略,有两种情况,第一种是当我们调用 shutdown 等方法关闭线程池后,这 
时候即使线程池内部还有没执行完的任务正在执行,但是由于线程池已经关闭,我们再继续想线程 
池提交任务就会遭到拒绝。另一种情况就是当达到最大线程数,线程池已经没有能力继续处理新提 
交的任务时,这是也就拒绝


*/ 
  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

java 中常见的几种线程池

// 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,
//若无可回收,则新建线程。
        Executors.newCachedThreadPool();//

        //创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
        Executors.newFixedThreadPool(10);

        //创建一个定长线程池,支持定时及周期性任务执行。
        Executors.newScheduledThreadPool(10);// 核心线程数10

        //创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
        //保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
        Executors.newSingleThreadExecutor();

 

//看源码,解释一下这三个创建线程池方法的作用
Executors.newFixedThreadPool(1);
Executors.newCachedThreadPool();
Executors.newSingleThreadExecutor();

6.项目中线程池的使用?

  1. tomcat 自带线程池
  2. CompletableFuture 创建线程时指定线程池,防止创建线程过多
  CompletableFuture<Integer> task1 =  CompletableFuture.supplyAsync(()->{
                result.setCluesNum(reportMpper.getCluesNum(beginCreateTime, endCreateTime, username));
                return null;
            },指定线程池);

案例:CompletableFuture异步和线程池讲解 - 不懒人 - 博客园

7 synchronized

synchronized 锁释放时机

● 当前线程的同步方法、代码块执行结束的时候释放

1) 正常结束

2) 异常结束出现未处理的error或者exception导致异常结束的时候释放

● 程序执行了 同步对象 wait 方法 ,当前线程暂停,释放锁

8. Sychronized和ReentrantLock的区别

  1. sychronized是⼀个关键字,ReentrantLock是⼀个类
  2. sychronized的底层是JVM层⾯的锁(底层由C++ 编写实现),ReentrantLock是API层⾯的锁 (java 内部的一个类对象)
  3. sychronized会⾃动的加锁与释放锁,ReentrantLock需要程序员⼿动加锁与释放锁
  4. sychronized是⾮公平锁,ReentrantLock可以选择公平锁或⾮公平锁

注: 假设多个线程都要获取锁对象,满足先等待的线程先获得锁则是公平锁,否则是非公平锁

  1. sychronized锁的是对象,锁信息保存在对象头中,ReentrantLock通过代码中int类型的state标识

来标识锁的状态

  1. sychronized底层有⼀个锁升级的过程(访问对象线程数由少到多,竞争由不激烈到激烈,底层会通过一种锁升级机制 无锁->偏向锁->轻量级锁->重量级锁,保证性能) ,会使用自旋 线程频繁等待唤醒会浪费性能,特别是锁的获取也许只需要很短的时间 ,不限于等待,直接执行简单代码while(true)执行完抢锁 来优化性能

代码演示

可重入演示

public static void main(String[] args) {
        // 可重入锁演示
            save();
    }
    public synchronized static void save() {
        System.out.println("save");
        update();
    }
    public synchronized static void update() {
        System.out.println("update");
    }

ReentrantLock 使用演示

public class TestDemo {
    public static void main(String[] args)throws Exception {
        ReentrantLock lock = new ReentrantLock();
        // 线程1
        new Thread(()->{
            lock.lock(); // 加锁
            add();
            lock.unlock();// 解锁
        }).start();
        // 线程2
        new Thread(()->{
            lock.lock(); // 加锁
            add();
            lock.unlock();// 解锁
        }).start();
    Thread.sleep(3000);
        System.out.println(i);
    }
    static int i =0;
    public  static void add() {
        i++;
    }
}

公平/非公平锁演示

package com.huike;

import java.util.concurrent.locks.ReentrantLock;

public class TestDemo {
    public static void main(String[] args)throws Exception {
        ReentrantLock lock = new ReentrantLock(false);
        // 线程1
        new Thread(()->{
            lock.lock(); // 加锁
            add();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            lock.unlock();// 解锁
        },"t1").start();



        // 线程2
        new Thread(()->{
            lock.lock(); // 加锁
            add();
            lock.unlock();// 解锁
        },"t2").start();

        new Thread(()->{
            lock.lock(); // 加锁
            add();
            lock.unlock();// 解锁
        },"t3").start();

        for (int j = 0; j < 100000; j++) {
            new Thread(()->{
                lock.lock(); // 加锁
                add();
                lock.unlock();// 解锁
            }).start();
        }




    Thread.sleep(30000);
        System.out.println(i);
    }
    static int i =0;
    public  static void add() {
        i++;
        System.out.println(Thread.currentThread().getName()+"获得了锁");
    }
}

10. 悲观锁 vs 乐观锁

要求

  • 掌握悲观锁和乐观锁的区别

对比悲观锁与乐观锁

  • 悲观锁的代表是 synchronized 和 Lock 锁
    • 其核心思想是【线程只有占有了锁,才能去操作共享变量,每次只有一个线程占锁成功,获取锁失败的线程,都得停下来等待】
    • 线程从运行到阻塞、再从阻塞到唤醒,涉及线程上下文切换,如果频繁发生,影响性能
    • 实际上,线程在获取 synchronized 和 Lock 锁时,如果锁已被占用,都会做几次重试操作,减少阻塞的机会
  • 乐观锁的代表是 AtomicInteger AtomicStampReference,使用 cas 来保证原子性
    • 其核心思想是【无需加锁,每次只有一个线程能成功修改共享变量,其它失败的线程不需要停止,不断重试直至成功】
    • 由于线程一直运行,不需要阻塞,因此不涉及线程上下文切换
    • 它需要多核 cpu 支持,且线程数不应超过 cpu 核数

12. ConcurrentHashMap的原理

12.1 JDK1.7

  • 数据结构:Segment(大数组) + HashEntry(小数组) + 链表,每个 Segment 对应一把锁,如果多个线程访问不同的 Segment,则不会冲突
  • 并发度:Segment 数组大小即并发度,决定了同一时刻最多能有多少个线程并发访问。Segment 数组不能扩容,意味着并发度在 ConcurrentHashMap 创建时就固定了(默认16,可以指定)
  • 扩容:每个小数组的扩容相对独立,小数组在超过扩容因子时会触发扩容,每次扩容翻倍
  • 其他Segment首次创建小数组时,会以Segment[0] 为原型为依据,数组长度,扩容因子都会以原型为准

12.2 JDK1.8

  • 数据结构:Node 数组 + 链表或红黑树,数组的每个头节点作为锁,如果多个线程访问的头节点不同,则不会冲突
  • 并发度:Node 数组有多大,并发度就有多大,与 1.7 不同,Node 数组可以扩容
  • 扩容条件:Node 数组满 3/4 时就会扩容(0.75 扩容因子)
  • 扩容时并发 get
    • 根据是否为 ForwardingNode 来决定是在新数组查找还是在旧数组查找,不会阻塞
  • 扩容时并发 put
    • 如果 put 的线程与扩容线程操作的链表是同一个,put 线程会阻塞

13. Hashtable 和 ConcurrentHashMap 有什么区别?其底层实现是什

么?

  • Hashtable 与 ConcurrentHashMap 都是线程安全的 Map 集合
  • Hashtable 并发度低,整个 Hashtable 对应一把锁,同一时刻,只能有一个线程操作它
  • ConcurrentHashMap 并发度高,整个 ConcurrentHashMap 对应多把锁,只要线程访问的是不同锁,那么不会冲突

14. ThreadLocal

作用

  • ThreadLocal 可以实现【资源对象】的线程隔离,让每个线程各用各的【资源对象】,避免争用引发的线程安全问题
  • ThreadLocal 同时实现了线程内的资源共享

原理

1. ThreadLocal是Java中所提供的线程本地存储机制,可以利⽤该机制将数据缓存在某个线程内部,

该线程可以在任意时刻、任意⽅法中获取缓存的数据

2. ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对

象)中都存在⼀个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的

3. 如果在线程池中使⽤ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使⽤完之后,应该要

把设置的key,value,也就是Entry对象进⾏回收,但线程池中的线程不会回收,,⽽一般我们使用ThreadLocal时都是使用static 修饰,导致线程对象是通过 强引⽤指向ThreadLocalMap,ThreadLocalMap也是通过强引⽤指向Entry对象,线程不被回收,

Entry对象也就不会被回收,从⽽出现内存泄漏,解决办法是,在使⽤了ThreadLocal对象之后,⼿

动调⽤ThreadLocal的remove⽅法,⼿动清除Entry对象

4. ThreadLocal经典的应⽤场景就是连接管理(⼀个线程持有⼀个连接,该连接对象可以在不同的⽅

法之间进⾏传递,线程之间不共享同⼀个连接)

  1. 项目中的使用: 项目中使用拦截器拦截请求后获取用户信息后放入ThreadLocal,然后在controller或service 获取用户信息
public class UserHolder {
    // 这里使用static 修饰,会导致存储在 ThreadLocal 对象不会被回收,需要每次用完都 remove
    private static final ThreadLocal<Long> tl = new ThreadLocal<>();

    public static void setUser(Long userId) {
        tl.set(userId);
    }
    public static Long getUser() {
        return tl.get();
    }
    public static void removeUser(){
        tl.remove();
    }
}
----------------我们曾经写的拦截器中是有remove 的-----------------------------------------------------

public class UserInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 前置逻辑校验
         UserHolder.setUser(xxxx);
        // 放行
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
      // 因为Tomcat 有线程池,多个线程会公用同一个 变量,为防止内存泄漏,使用完毕后 需要清理
        UserHolder.removeUser();
    }
}

ThreadLocalMap 的一些特点

  • key 的 hash 值统一分配
  • 初始容量 16,扩容因子 2/3,扩容容量翻倍
  • key 索引冲突后用开放寻址法解决冲突

弱引用 key

ThreadLocalMap 中的 key 被设计为弱引用,原因如下

  • Thread 可能需要长时间运行(如线程池中的线程),如果 key 不再使用,需要在内存不足(GC)时释放其占用的内存

内存释放时机

  • 被动 GC 释放 key
    • 仅是让 key 的内存释放,关联 value 的内存并不会释放
  • 懒惰被动释放 value
    • get key 时,发现是 null key,则释放其 value 内存
    • set key 时,会使用启发式扫描,清除临近的 null key 的 value 内存,启发次数与元素个数,是否发现 null key 有关
  • 主动 remove 释放 key,value
    • 会同时释放 key,value 的内存,也会清除临近的 null key 的 value 内存
    • 推荐使用它,因为一般使用 ThreadLocal 时都把它作为静态变量(即强引用),因此无法被动依靠 GC 回收

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

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

相关文章

学习.NET MAUI Blazor(一)、Blazor是个啥?

先把Blazor放一边&#xff0c;先来看看目前Web开发的技术栈。 注&#xff1a;上图只是为了说明问题&#xff0c;没有任何语言歧视&#xff01; 这是目前最常用的前后端分离开发模式&#xff0c;这个开发模式需要配备前端工程师和后端工程师。当然了&#xff0c;全栈工程师另当…

GNN Algorithms(2): GCN, Graph Convolutional Network

GCN Algorithm Essence: 传统的卷积运算无法在graph上进行运算&#xff0c;所有借由傅里叶变换Fourier Transformation实现在graph上的卷积运算&#xff0c;得到graph convolution&#xff0c;退出GCN(graph convolutional network)。 Background graph embedding&#xff1…

mysql优化——mycat数据库中间件

文章目录 目录 文章目录 前言 一、mycat原理 二、mycat完成读写分离 2.1搭建MySQL的主从模式 2.2实现读写分离 三、mycat垂直分库 四、水平分表 五、水平拆分表的连表操作 六、全局表 总结 前言 Mycat是数据库中间件,所谓中间件,是一类连接软件组件和应用的计算机软件,以…

CUDA入门和网络加速学习(三)

0. 简介 最近作者希望系统性的去学习一下CUDA加速的相关知识&#xff0c;正好看到深蓝学院有这一门课程。所以这里作者以此课程来作为主线来进行记录分享&#xff0c;方便能给CUDA网络加速学习的萌新们去提供一定的帮助。 1. CUDA中的Stream和Event 1.1 CUDA stream CUDA s…

蓝桥杯C/C++百校真题赛(1期)Day4题解(左孩子右兄弟、作物杂交)

Q1 左孩子右兄弟 f[u]表示以u为根转化而成的二叉树&#xff08;以下简称二叉树)的最大高度f[u]max(f[ji])cnt[u]−11,ji是u的所有儿子&#xff0c;cnt[u]表示原树中u的儿子个数。因为以u为根的二叉树肯定由u的一个儿子为根的二叉树构成来作为他的左半部假设f[jt]是最大的那个,那…

A Joint Neural Model for Information Extraction with Global Features论文解读

A Joint Neural Model for Information Extraction with Global Features code&#xff1a;BLENDER Lab | Software (illinois.edu)或者GerlinGreen/OneIE: Forked from OneIE: A Joint Neural Model for Information Extraction with Global Features (github.com) paper&…

超宽带UWB无线技术,厘米级测距定位,精准位置确定方案应用

UWB标签和锚点之间的双向测距测量它们的相对距离&#xff0c;这种方法用于例如安全访问应用程序&#xff0c;其中将根据标签设备的接近程度授予访问权限。 UWB不能被简单的中继攻击欺骗&#xff0c;因为它的距离测量不是基于信号强度&#xff0c;而是基于时间&#xff0c;中继…

git初识(四)

rebase(变基) 它能让git提交记录简洁 多个记录整合成一个记录 git rebase -i HEAD~3从最新记录开始依次找3条记录进行合并 git rebase -i 版本号从该版本号开始一直合并到最新记录 在做记录合并时&#xff0c;建议不要和已push到仓库的记录 beyond compare 在git的使用过程…

我国盆栽蔬菜行业发展势头向好 未来市场需求将保持持续攀升态势

盆栽蔬菜是指在花盆或其他容器内种植的蔬菜&#xff0c;供人观赏和采摘食用。盆栽蔬菜分为观果类蔬菜、彩色蔬菜、绿叶保健蔬菜、根茎类蔬菜等几类。其中观果类蔬菜包括彩色甜椒、矮生番茄、樱桃番茄、硬果番茄、观赏茄子、小型辣椒、袖珍西瓜及各种南瓜、甜瓜、西葫芦、黄秋葵…

Python 为什么如此设计?

大概两年半前&#xff0c;我萌生了要创作一个新的系列文章的想法&#xff0c;也就是“Python为什么”&#xff0c;试图对 Python 的语法及特性提出“为什么”式的问题&#xff0c;以此加深对它的理解&#xff0c;探寻使用技巧、发展演变、设计哲学等话题。 一直以来&#xff0…

智芯传感微差压气体压力传感器成功入围第三届“SIA感知领航优秀项目征集”年度杰出产品及技术成长型企业组

近日&#xff0c;由中国传感器与物联网产业联盟开展的第三届“SIA感知领航优秀项目征集”结果正式出炉&#xff0c;北京智芯传感科技有限公司&#xff08;以下简称“智芯传感”&#xff09;作为国内领先的MEMS传感器厂商&#xff0c;凭借旗下产品“微差压气体压力传感器”的卓越…

uniapp的生命周期

目录 一、应用生命周期 只能在 App.vue 里执行 1、onLaunch 2、onShow 3、onHide 二、页面生命周期 1、onLoad 监听页面加载 2、onReady 3、onShow 监听页面显示 4、onHide 监听页面隐藏 5、onUnload 监听页面卸载 三、组件生命周期 1、beforeCreate 2、…

Java培训Mycat安装启动

一、 安装启动 1、安装解压 解压缩文件拷贝到linux下 /usr/local/ 2、配置文件介绍 schema.xml 定义逻辑库&#xff0c;表、分片节点等内容 rule.xml 定义分片规则 server.xml 定义用户以及系统相关变量&#xff0c;如端口等 3、 配置文件修改 <?xml version”1.0…

MFC中窗口隐藏后再显示,子窗口没刷新(WS_CLIPCHILDREN,WS_CLIPSIBLINGS)

问题描述 非模态对话框下&#xff0c;显示了子窗口&#xff0c;但窗口隐藏后&#xff0c;再通过任务栏显示出来时&#xff0c;子窗口内容就不显示了。 正确的显示如下&#xff1a; 显示异常的情况 - 原因分析 显示父窗口时&#xff0c;父窗口会进行重绘&#xff08;我的理…

什么是数字化?企业该如何做数字化?

现在大家都在说数字化&#xff0c;数字化到底是什么&#xff1f; 数字化不是万能&#xff0c;只能赋能&#xff0c;数字化转型也不是口号&#xff0c;而是需要在具体规划和业务发展中深入思考和切实贯彻。 如今数字化俨然成为炙手可热的一个高频词汇&#xff0c;不论个人还是…

MySQL#2(数据模型,SQL通用语法,SQL分类)

目录 一.数据模型 二.SQL通用语法 三.SQL的分类 1.DDL DDL---操作数据库 DDL---操作表 2.DML DML---操作数据 3.DQL(重点) 基础查询 条件查询 排序查询 分组查询 分页查询 扩展: 聚合函数 一.数据模型 数据库在内存中是以文件夹的方式存在 数据表和数据是以文件的形式存…

有利润表模板的BI软件有哪些?

要分析利润&#xff0c;就需要制作利润表。有哪些可套用利润表模板的BI软件&#xff1f;关于这个问题&#xff0c;找奥威BI软件就对了。奥威BI软件有一套标准化的数据分析方案&#xff0c;内设数十张数据可视化分析报表模板&#xff0c;其中就包括了利润表模板&#xff0c;下载…

KNN算法预测哪些用户会购买这种全新SUV。并且在最后一列用来表示用户是否购买 项目实战代码+数据

K近邻算法通过计算被分类对象与训练集对象之间的距离,确定其k个临近点,然后使用这k个临近点中最多的分类作为分类结果。 如上图,当K=3时,它会被分类为 Class B。因为K=3时,3个临近点里有2个是B类的。 同理,K=7时它会被分类为 Class A,因为K=7时,7个临近点里4个是A类的…

C. The Third Problem(MEX,思维,组合数)[Codeforces Round #804 (Div. 2)]

题面如下&#xff1a; 思路or题解 因为是MEXMEXMEX问题&#xff0c;我们可以通过维护每一个数的 [l,r][l, r][l,r] 来进行求解 如果 kkk 维护的范围是 [l,r][l, r][l,r] 那么我们在计算 k1k 1k1 的时候可以发现&#xff1a; 在[l1,r−1][l 1, r - 1][l1,r−1] 如果 k1k 1k1…

CS61A 2022 fall lab0

CS61A 2022 fall lab0:Getting Started 不得不感叹实验网站是真的高级… 我打算用ubuntu做实验 文章目录CS61A 2022 fall lab0:Getting StartedIntroductionSetup1.Install a terminal2.Install Python 3Python3.7基于update-alternatives的Python默认版本系统级修改&#xff1…