剑指JUC原理-3.线程常用方法及状态

news2024/12/24 2:22:04

常用方法

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

start和run

调用run

public static void main(String[] args) {
 Thread t1 = new Thread("t1") {
 @Override
 	public void run() {
 		log.debug(Thread.currentThread().getName());
 		FileReader.read(Constants.MP4_FULL_PATH);
 	}
 };
 t1.run();
 log.debug("do other things ...");
}

输出:

19:39:14 [main] c.TestStart - main
19:39:14 [main] c.FileReader - read [1.mp4] start ...
19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms
19:39:18 [main] c.TestStart - do other things ...

程序仍在 main 线程运行, FileReader.read() 方法调用还是同步的

调用start

将上述代码的 t1.run() 改为 t1.start

输出:

19:41:30 [main] c.TestStart - do other things ...
19:41:30 [t1] c.TestStart - t1
19:41:30 [t1] c.FileReader - read [1.mp4] start ...
19:41:35 [t1] c.FileReader - read [1.mp4] end ... cost: 4542 ms

此时再t1线程执行,而FileReader.read() 方法调用是异步的

总结

直接调用run是在主线程中执行了run,并没有启用新的线程,而使用start是启动新的线程,通过新的线程简介执行了run中的代码。

sleep和yield

sleep

**调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)**以一段代码为例

		Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        t1.start();

        System.out.println("t1 state: "+ t1.getState());

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1 state: "+ t1.getState());

输出:

t1 state: RUNNABLE
t1 state: TIMED_WAITING

其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException

		Thread t1 = new Thread("t1") {
            @Override
            public void run() {
                log.debug("enter sleep...");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    log.debug("wake up...");
                    e.printStackTrace();
                }
            }
        };
        t1.start();

        Thread.sleep(1000);
        log.debug("interrupt...");
        t1.interrupt();

输出:

enter sleep...
wake up...
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at test2$1.run(test2.java:10)

睡眠结束后的线程未必会立刻得到执行

睡眠结束,可能还在处理其他的线程,需要等待cpu调度器调度

yield

本质上是让出cpu的使用权, 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程。具体的实现依赖于操作系统的任务调度器

线程优先级

线程优先级会提示(hint)调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它

如果 cpu 比较忙,那么优先级高的线程会获得更多的时间片,但 cpu 闲时,优先级几乎没作用

join方法详解

为什么需要join?

下面的代码执行,打印 r 是什么?

		log.debug("开始");
        Thread t1 = new Thread(() -> {
            log.debug("开始");
            sleep(1);
            log.debug("结束");
            r = 10;
        },"t1");
        t1.start();
        // t1.join();
        log.debug("结果为:{}", r);
        log.debug("结束");

分析:

  • ​ 因为主线程和线程 t1 是并行执行的,t1 线程需要 1 秒之后才能算出 r=10
  • ​ 而主线程一开始就要打印 r 的结果,所以只能打印出 r=0

解决方案:

  • ​ 用 sleep 行不行?为什么?不能精准的把控执行结束的时间。
  • ​ 用 join,加在 t1.start() 之后即可**(可选)**

以调度的角度来讲,如果

  • 需要等待结果返回,才能继续运行就是同步
  • 不需要等待结果返回,就能继续运行就是异步

在这里插入图片描述

等待多个结果

Thread t1 = new Thread(() -> {
 	sleep(1);
 	r1 = 10;
});
Thread t2 = new Thread(() -> {
 	sleep(2);
 	r2 = 20;
});
long start = System.currentTimeMillis();
t1.start();
t2.start();

t1.join();
t2.join();

long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);

分析:

实际上,2s执行完成,为什么呢?因为 两个线程都是同时启动的,无论t1.join 和 t2.join那个在前那个在后,其最终都会对两个线程分别的阻塞等待,只因为两个线程是同时启动的。

所以结果时不变的,效果图如下:

在这里插入图片描述

有时效的join

等待时间

 Thread t1 = new Thread(() -> {
 	sleep(1);
 	r1 = 10;
 });
 long start = System.currentTimeMillis();
 t1.start();
 // 线程执行结束会导致 join 结束
 t1.join(1500);
 long end = System.currentTimeMillis();
 log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);

输出:

20:48:01.320 [main] c.TestJoin - r1: 10 r2: 0 cost: 1010

其实就是如果 执行完时间在 join 等待时间内,那么join同步阻塞就是有效的,否则

没等够时间

 Thread t1 = new Thread(() -> {
 	sleep(2);
 	r1 = 10;
 });
 long start = System.currentTimeMillis();
 t1.start();
 // 线程执行结束会导致 join 结束
 t1.join(1500);
 long end = System.currentTimeMillis();
 log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);

输出:

20:52:15.623 [main] c.TestJoin - r1: 0 r2: 0 cost: 1502

interrupt 方法详解

打断 sleep,wait,join 的线程 ,这几个方法都会让线程进入阻塞状态,而打断阻塞状态的线程,会清空打断状态,同时抛出一个 InterruptedException 异常。

打断标记

在 Java 中,每个线程都有一个称为 “打断标记”(interrupt flag)的状态位。这个状态位用于标识线程是否被请求中断。

当一个线程通过调用另一个线程的 interrupt() 方法来请求中断时,被请求中断的线程的打断标记会被设置为 “true”。这意味着被请求中断的线程可以检查自己的打断标记来判断是否被中断,并根据需要采取相应的操作。

打断标记不会直接中断线程的执行,而是提供了一种机制,让线程能够感知到中断请求并根据情况作出响应。具体的响应方式由线程自身决定,可以是终止线程、抛出异常或执行其他适当的操作。

在 Java 中,可以使用 Thread.interrupted() 方法来检查当前线程的打断标记,并清除标记(将其设置为 “false”)。还可以使用 Thread.isInterrupted() 方法来检查线程的打断标记,但不会清除标记。

需要注意的是,打断标记只是一个指示线程是否被请求中断的标志,它不会自动中断线程的执行。线程在执行过程中需要自行检查打断标记,并根据需要采取相应的操作来处理中断请求。

打断阻塞线程

 Thread t1 = new Thread(()->{
	 sleep(1);
 }, "t1");
 t1.start();
 sleep(0.5);
 t1.interrupt();
 log.debug(" 打断状态: {}", t1.isInterrupted());

输出:

java.lang.InterruptedException: sleep interrupted
 at java.lang.Thread.sleep(Native Method)
 at java.lang.Thread.sleep(Thread.java:340)
 at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
 at cn.itcast.n2.util.Sleeper.sleep(Sleeper.java:8)
 at cn.itcast.n4.TestInterrupt.lambda$test1$3(TestInterrupt.java:59)
 at java.lang.Thread.run(Thread.java:745)
21:18:10.374 [main] c.TestInterrupt - 打断状态: false

此时可以看到,不仅抛出 InterruptedException 异常,同时打断状态设置为 false

打断正常线程

使用 interrupt() 方法来打断正在运行的线程不会立即停止线程的执行。它只是设置了线程的打断标记为 “true”,并且如果线程处于等待状态(如 sleep()、wait()、join() 等),它会立即抛出 InterruptedException 异常。

打断正常运行的线程, 不会清空打断状态

 Thread t2 = new Thread(()->{
 	while(true) {
 		Thread current = Thread.currentThread();
 		boolean interrupted = current.isInterrupted();
		 if(interrupted) {
 			log.debug(" 打断状态: {}", interrupted);
 			break;
 		}
 	}
 }, "t2");
 t2.start();
 sleep(0.5);
 t2.interrupt();

输出:

20:57:37.964 [t2] c.TestInterrupt - 打断状态: true 

终止模式之两阶段终止模式

在一个线程 T1 中如何“优雅”终止线程 T2?这里的【优雅】指的是给 T2 一个料理后事的机会。

使用线程对象的stop方法停止线程

stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它线程将永远无法获取锁!!1 所以这个方法不妥

使用 System.exit(int) 方法停止线程

目的仅是停止一个线程,但这种做法会让整个程序都停止

两阶段终止模式

以一个监控系统为例

在这里插入图片描述

如果有被打断,那么就料理后事,结束循环,如果没有被打断,那么 首先睡眠 2s,如果无异常,那么就正常的执行监控记录,然后继续循环,如果有异常,因为 使用interrupt 如果线程是阻塞的,会抛出 InterruptedException异常同时 重置标志位,所以需要重新设置打断标记。

public void start(){
        thread = new Thread(() -> {
            while(true) {
                Thread current = Thread.currentThread();
                if(current.isInterrupted()) {
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000);
                    log.debug("将结果保存");
                } catch (InterruptedException e) {
                    // 打断 sleep后会清除 打断标记,所以需要重新设置打断标记
                    current.interrupt();
                }

            }
        },"监控线程");
        thread.start();
    }

    public void stop() {
        thread.interrupt();
    }

输出:

11:49:42.915 c.TwoPhaseTermination [监控线程] - 将结果保存
11:49:43.919 c.TwoPhaseTermination [监控线程] - 将结果保存
11:49:44.919 c.TwoPhaseTermination [监控线程] - 将结果保存
11:49:45.413 c.TestTwoPhaseTermination [main] - stop 
11:49:45.413 c.TwoPhaseTermination [监控线程] - 料理后事

主线程与守护线程

默认情况下,Java 进程需要等待所有线程都运行结束,才会结束。有一种特殊的线程叫做守护线程,只要其它非守护线程运行结束了,即使守护线程的代码没有执行完,也会强制结束。(默认情况下,主线程是守护线程还是非守护线程)

log.debug("开始运行...");
Thread t1 = new Thread(() -> {
 	log.debug("开始运行...");
 	sleep(2);
 	log.debug("运行结束...");
}, "daemon");
// 设置该线程为守护线程
t1.setDaemon(true);
t1.start();
sleep(1);
log.debug("运行结束...");

输出:

08:26:38.123 [main] c.TestDaemon - 开始运行... 
08:26:38.213 [daemon] c.TestDaemon - 开始运行... 
08:26:39.215 [main] c.TestDaemon - 运行结束... 

注意:

垃圾回收器线程就是一种守护线程

Tomcat 中的 Acceptor 和 Poller 线程都是守护线程,所以 Tomcat 接收到 shutdown 命令后,不会等待它们处理完当前请求

原理解读:

这句话的意思是,在Tomcat服务器中,Acceptor(接收器)和Poller(轮询器)线程都被设置为守护线程。当Tomcat接收到关闭命令时,它不会等待这些线程处理完当前正在进行的请求。

在Tomcat中,Acceptor线程负责接收新的连接请求,而Poller线程负责处理已建立的连接上的I/O操作。由于它们是守护线程,它们的运行不会阻止Tomcat服务器的关闭过程。

当接收到关闭命令时,Tomcat会立即停止接受新的连接请求,并开始关闭已经建立的连接。但是,由于Acceptor和Poller线程是守护线程,它们可能无法处理完当前正在进行的请求,因为守护线程会随着主线程的结束而立即停止。

因此,这句话的含义是,Tomcat在接收到关闭命令后,不会等待Acceptor和Poller线程处理完当前请求,而是立即停止它们的运行,以便尽快关闭服务器。

五种状态

这是从 操作系统 层面来描述的

在这里插入图片描述

【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联(相当于 new了线程还没有start)

【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行

【运行状态】指获取了 CPU 时间片运行中的状态,当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换

【阻塞状态】

  • 如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入
    【阻塞状态】
  • 等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】
  • 与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑
    调度它们

【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

六种状态

这是从Java API层面上来描述

根据 Thread.State 枚举,分为六种状态

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }

这也是为什么能分成六种状态的原因

在这里插入图片描述

  • NEW 线程刚被创建,但是还没有调用 start() 方法
  • RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的
    【可运行状态】、【运行状态】和【阻塞状态】(在Java中,阻塞状态可以由多种原因引起,例如线程等待锁、等待输入/输出完成等。需要注意的是,由于Java的I/O模型通常使用阻塞式的BIO(Blocking I/O),所以在Java中无法区分不同类型的阻塞状态,仍然将其归类为RUNNABLE状态)
  • BLOCKED , WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分
  • TERMINATED 当线程代码运行结束

以一段代码来详解这六种状态吧。

		Thread t1 = new Thread("t1") {
            @Override
            public void run() {

                System.out.println("running...");
            }
        };

        Thread t2 = new Thread("t2") {
            @Override
            public void run() {
                while(true) { // runnable

                }
            }
        };
        t2.start();

        Thread t3 = new Thread("t3") {
            @Override
            public void run() {

                System.out.println("running...");
            }
        };
        t3.start();

        Thread t4 = new Thread("t4") {
            @Override
            public void run() {
                synchronized (test.class) {
                    try {
                        Thread.sleep(1000000); // timed_waiting
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t4.start();

        Thread t5 = new Thread("t5") {
            @Override
            public void run() {
                try {
                    t2.join(); // waiting
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        t5.start();

        Thread t6 = new Thread("t6") {
            @Override
            public void run() {
                // t4先上锁,t6就拿不到了
                synchronized (test.class) { // blocked
                    try {
                        Thread.sleep(1000000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        t6.start();

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1 state "+ t1.getState());
        System.out.println("t2 state "+ t2.getState());
        System.out.println("t3 state "+ t3.getState());
        System.out.println("t4 state "+ t4.getState());
        System.out.println("t5 state "+ t5.getState());
        System.out.println("t6 state "+ t6.getState());

输出:

t1 state NEW
t2 state RUNNABLE
t3 state TERMINATED
t4 state TIMED_WAITING
t5 state WAITING
t6 state BLOCKED

以这段代码为例,t1其实就是新建状态,t2相当于正在运行中,t3相当于运行结束,t456相当于阻塞的三种状态,4是 sleep阻塞,5是join阻塞,6是获取不到锁阻塞。

各状态资源占用情况

在Java线程的各个状态中,不同状态下线程所占用的资源情况如下:

  1. 新建状态(New):在新建状态下,线程并不占用任何系统资源,只是占用了一些内存空间来存储线程对象本身的信息。
  2. 可运行状态(Runnable):在可运行状态下,线程占用了一些系统资源,包括程序计数器、虚拟机栈和一些线程私有数据。这些资源主要用于保存线程的执行上下文和局部变量等信息。
  3. 运行状态(Running):在运行状态下,线程会占用CPU资源,以便执行线程的任务。此时,除了占用的CPU资源外,线程还会继续占用可运行状态下的资源。
  4. 阻塞状态(Blocked):在阻塞状态下,线程暂时不占用CPU资源,但仍然占用了一些系统资源。具体资源的占用情况取决于线程被阻塞的原因。例如,如果线程因为等待获取锁而被阻塞,那么它会占用一定数量的锁资源和等待队列。
  5. 等待状态(Waiting):在等待状态下,线程通常不占用CPU资源和锁资源,但仍然占用了一些系统资源。这些资源包括等待队列、条件变量和一些其他线程同步机制所需的资源。
  6. 终止状态(Terminated):在终止状态下,线程不再占用任何系统资源。它的执行上下文和局部变量等信息都会被释放,线程对象本身也可以被垃圾回收。

需要注意的是,不同状态下线程所占用的资源情况是动态变化的。线程的状态会根据线程的调度、等待条件的满足以及任务的完成而发生变化。因此,在编写多线程程序时,需要注意合理管理线程的状态和资源,以避免资源的浪费和性能的下降。

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

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

相关文章

[已解决]安装的明明是pytorch-gpu,但是condalist却显示cpu版本,而且torch.cuda.is_available 也是flase

问题; 安装了gpu版本的pytorch&#xff0c;但是显示的torch.cuda.is_available(&#xff09;却是flase。 conda list查看 版本显示只有cpuonly 在网上找了半天&#xff0c;也没有解决办法。 仔细看了一下&#xff0c;发现&#xff0c;有个单独的包叫cpuonly&#xff0c;不知道…

【工具问题】IDEA每次关闭的时候都会弹框显示closing project,然后弹框持续很久就像卡住了

idea关闭的时候出现问题 问题展示为什么会出现这种情况怎么解决 问题展示 我idea已经关闭了&#xff0c;但是这个弹框要持续很久才能关闭 为什么会出现这种情况 我的plugins原本是加载不出来的&#xff0c;所以我按照网上说法去做 怎么解决 file->setting,再如图选择…

C语言KR圣经笔记 2.4声明 2.5算术操作符 2.6关系和逻辑操作符

2.4 声明 所有变量在使用前必须声明&#xff0c;尽管某些特定的声明可以由上下文隐式地做出。声明指定了类型&#xff0c;并包含一个或多个该类型的变量列表&#xff0c;如 int lower, upper, step; char c, line[1000]; 多个变量能以任何方式分布在多个声明之中&#xff1b…

SpringBoot整合MyBatis-Plus详解(一)

文章目录 SpringBoot整合MyBatis-Plus详解&#xff08;一&#xff09;MyBatis-Plus简介入门案例开发环境准备环境准备创建数据库和表Pom.xml 编写代码application.properties启动类User实体类创建UserMapper接口入门测试案例添加日志功能&#xff08;在application.properties后…

DVWA-Cross Site Request Forgery (CSRF)

大部分网站都会要求用户登录后,使用相应的权限在网页中进行操作,比如发邮件、购物或者转账等都是基于特定用户权限的操作。浏览器会短期或长期地记住用户的登录信息,但是,如果这个登录信息被恶意利用呢?就有可能发生CSRF CSRF的英文全称为Cross Site Request Forgery,中文…

openEuler 22.03 x86架构下docker运行arm等架构的容器——筑梦之路

为什么要这样做&#xff1f; 随着国产化的普及&#xff0c;国家政策对信创产业的支持&#xff0c;尤其一些金融证券行业、政府单位等&#xff0c;逐渐开始走国产化信创的路线&#xff0c;越来越多接触到国产 CPU &#xff08;arm 平台&#xff0c;比如华为的鲲鹏处理器&#xf…

TSINGSEE青犀基于AI视频识别技术的平安校园安防视频监控方案

一、背景需求 因学校频频出治安事件&#xff0c;所以必须要加强学校的安防工作&#xff0c;目前来看&#xff0c;大部分校园都建设了视频监控来预防保障校园安全。但是传统的视频监控系统&#xff0c;主要通过设备来录像以及人员时时监控来进行。这种监管方式效率十分低下&…

elementui时间日期组件右边自定义图标

效果 改为 首先是将左边的清除图标关闭 然后是将右边的图标设置为display&#xff1a;none,设置宽度&#xff0c;左右内边距 最后是 mounted() {/*思路&#xff1a;通过document文档&#xff0c;选中日期时间选择器元素&#xff0c;然后创建一个i标签&#xff0c;并指定其类…

分布式日志和链路追踪

分布式日志 实现思路 分布式日志框架服务的实现思路基本是一致的&#xff0c;如下&#xff1a; 日志收集器&#xff1a;微服务中引入日志客户端&#xff0c;将记录的日志发送到日志服务端的收集器&#xff0c;然后以某种方式存储数据存储&#xff1a;一般使用ElasticSearch分…

简化路径(C++解法)

题目 给你一个字符串 path &#xff0c;表示指向某一文件或目录的 Unix 风格 绝对路径 &#xff08;以 / 开头&#xff09;&#xff0c;请你将其转化为更加简洁的规范路径。 在 Unix 风格的文件系统中&#xff0c;一个点&#xff08;.&#xff09;表示当前目录本身&#xff1…

【高阶数据结构】并查集和图

目录 1.数据结构--并查集 2.数据结构--图 1.图的基础概念 2.图的简单实现 2.1.邻接矩阵的图实现 2.2.邻接表的图实现 2.3.图的DFS和BFS 2.4.最小生成树 2.4.1.Kruskal(克鲁斯卡尔算法) 2.4.2.Prim&#xff08;普里姆算法&#xff09; 2.5.最短路径 2.5.1.Dijkstra(…

非遗主题网站的设计与实现基于PHP实现

包括源码参考论文 下载地址: https://juzhendongli.store/commodity/details/18

k8s-----26、细粒度权限管理 RBAC

0、导读 每一个用户对API资源进行操作都需要通经过以下三个步骤: 第一步:对客户端访问进行认证操作,确认是否具有访问k8s权限(也就是通过serviceaccount) token(共享秘钥) SSL(双向SSL认证) …通过任何一个认证即表示认证通过,进入下一步第二步:授权检查,确认是否对资源…

openpnp - SlotSchultzFeeder source code bugfix

文章目录 openpnp - SlotSchultzFeeder source code bugfix概述笔记openpnp源码调试环境排查思路开git分支查到的问题 - 1查到的问题 - 2查到的问题 - 3针对以上问题进行的逻辑修正D:\my_openpnp\openpnp_github\src\main\java\org\openpnp\machine\reference\driver\wizards\G…

Redis快速上手篇(六)主从复制

主从复制 主机数据更新后根据配置和策略&#xff0c; 自动同步到备机的master/slaver机制&#xff0c;Master以写为主&#xff0c;Slave以读为主 读写分离&#xff0c;性能扩展&#xff08;主 写 从 读&#xff09; 容灾快速恢复 一主二仆 拷贝多个redis.conf文件include(写绝…

动态规划太难了?是你没有找对方法,四题带你搞懂动态规划!

&#x1f4af; 博客内容&#xff1a;动态规划刷题 &#x1f600; 作  者&#xff1a;陈大大陈 &#x1f680; 个人简介&#xff1a;一个正在努力学技术的准前端&#xff0c;专注基础和实战分享 &#xff0c;欢迎私信&#xff01; &#x1f496; 欢迎大家&#xff1a;这里是CS…

【RV1106/RV1103】RV1103增加RTL8723BS

文章目录 Kernel 部分支持配置 menuconfigWiFi 功能支持Bluetooth 功能支持 原SDK的Busybox配置BT 部分文件系统部分蓝牙测试dbus 安装测试遇到的问题让hci0出来 使用hcitool来操作 Kernel 部分支持 配置 menuconfig WiFi 功能支持 Bluetooth 功能支持 正基和海华的模块都使用…

pip 更换源

方案1 在C盘用户名录下新建pip文件夹&#xff0c;里面包含pip.ini文件 方案2 在C盘用户名目录的AppData的Roaming下新建pip文件夹&#xff0c;里面包含pip.ini文件。 内容为 [global] index-url https://pypi.tuna.tsinghua.edu.cn/simple

题目 1056: 二级C语言-温度转换(python详解)——练气四层初期

✨博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;算法修炼之练气篇&#xff08;C\C版&#xff09; &#x1f353;专栏&#xff1a;算法修炼之筑基篇&#xff08;C\C版&#xff09; &#x1f352;专栏&#xff1a;算法修炼之练气篇&#xff08;Python版&#xff09; ✨…

CVE-2022-22965 Spring Framework远程命令执行

0x01 影响版本 Spring Framework < 5.3.18 Spring Framework < 5.2.20 JDK>9 0x02 复现环境 vulhub/spring/cve-2022-22965 0x03 漏洞复现 首先docker-compose up -d开启靶场 输入payload <%if("j".equals(request.getParameter("pwd")…