3.Java线程

news2025/1/17 21:46:40

Java线程

3.1 创建和运行线程

方法一,直接使用Thread

import lombok.extern.slf4j.Slf4j;

/**
 * 使用匿名内部类创建线程
 * @author xc
 * @date 2023/4/30 16:19
 */
@Slf4j
public class Test1 {
    public static void main(String[] args) {
        Thread thread = new Thread(){
            @Override
            public void run() {
                log.debug("开启的新线程");
            }
        };
        thread.setName("子线程");
        thread.start();
        log.debug("main线程");
    }
}

img

方法二,Runable

import lombok.extern.slf4j.Slf4j;

/**
 * Runnable创建线程
 * @author xc
 * @date 2023/4/30 16:26
 */
@Slf4j
public class Test2 {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                log.debug("子线程运行");
            }
        };
        Thread thread = new Thread(r);
        thread.setName("子线程");
        thread.start();
        log.debug("主线程运行");
    }
}

看到有该注解,表示该接口是函数式注解

img

就可以使用Lambda表达式简化代码

import lombok.extern.slf4j.Slf4j;

/**
 * Runnable创建线程
 * @author xc
 * @date 2023/4/30 16:26
 */
@Slf4j
public class Test2 {
    public static void main(String[] args) {
        Runnable r = () -> log.debug("子线程运行");
        Thread thread = new Thread(r);
        thread.setName("子线程");
        thread.start();
        log.debug("主线程运行");
    }
}

img

小结

  • 方法一是把线程和任务合并在一起,方法二是把线程和任务分开
  • 用Runnable更容易与线程池等高级API配合
  • 用Runnable让任务脱离了Thread继承体系,更灵活

方法三,FutureTask和Thread配合

实现

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author xc
 * @date 2023/4/30 16:43
 */
@Slf4j
public class Test3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                log.debug("子线程运行");
                //打印完休息1s
                Thread.sleep(1000);
                return 100;
            }
        });
        Thread t = new Thread(task);
        t.start();
        log.debug("主线程运行");
        // 阻塞一直等到结果返回
        log.error("子线程返回结果是"+task.get());
    }
}

结果

img

3.2 观察多个线程同时运行

  • 交替执行
  • 谁先谁后,不由我们控制

代码

import lombok.extern.slf4j.Slf4j;

/**
 * @author xc
 * @date 2023/4/30 16:49
 */
@Slf4j
public class Test4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            for (int i = 0; i < 100; i++) {
                log.debug("子线程{}",i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        for (int i = 0; i < 100; i++) {
            log.debug("主线程{}",i);
            Thread.sleep(500);
        }
    }
}

结果

img

3.3 查看进程线程的方法

Windows

  • 任务管理器

img

  • tasklist 查看进程
  • taskkill 杀死进程

img

Linux

  • ps -ef 查看所有进程
  • ps -fT -p 查看某个进程的所有线程
  • kill 杀死进程
  • top 按大写H切换是否显示线程
  • top -H -P 查看某个进程的所有线程

Java

  • jps 命令查看所有Java进程

img

  • jstack 查看某个Java进程的所有线程状态
  • jconsole 来查看某个Java进程中线程的运行情况(图形界面)

img

3.4 原理之线程运行

栈与栈帧

我们都知道JVM由堆、栈、方法区组成,其中栈内存是给谁用的呢?每个线程启动后,虚拟机会自动分配一块栈内存

  • 每个栈由多个栈帧组成,对应着每次方法调用时所占用的内存
  • 每个线程只能有一个活动的栈帧,对于这当前正在执行的方法

img

栈帧图解

img

多线程中每个线程有自己的栈内存,又有多个栈帧,互不干扰

主线程

img

子线程

img

线程上下文切换

因为以下一些原因导致cpu不再执行当前线程,转而执行另一个线程的代码

  • 线程的cpu时间片用完
  • 垃圾回收
  • 有更高优先级的线程需要运行
  • 线程自己调用了sleep、yield、wait、join,park,synchronized、lock方法

当Context Switch发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,java中对应的就是程序计数器,它的作用就是记住下一条jvm指令的执行地址,是线程私有的

  • 状态包括程序计数器、虚拟机栈中的每个栈帧信息,如局部变量,操作数栈,返回地址等
  • Context Switch频繁发生会影响性能

3.5 常见方法

方法名static功能说明注意
start()启动一个新线程,在新的线程运行run方法中的代码start方法只是让线程进入就绪,里面代码不一定立刻运行(CPU的时间片还没分给它)。每个线程对象的start方法只能调用一次,如果调用了多次会出现IllegalThreadStateException
run()新线程启动后会调用的方法如果在构造Thread对象时传递了Runnable参数,则线程启动后会调用Runnable中的run方法,否则默认不执行任何操作。但可以创建Thread的之类对象,来覆盖默认行为
join()等待线程运行结束
join(long n)等待线程结束,最多等n毫秒
getId()获取线程长整型的idid唯一
getName()获取线程名
setName()修改线程名
getPriority()获取线程优先级
setPriority(int)修改线程优先级java中规定线程优先级时1~10的整数,较大的优先级能提高该线程被CPU调度的几率
getState()获取线程状态Java中线程状态是用6个enum表示,NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
isInterrupted()判断是否被打断不会清除打断标记
isAlive()线程是否存活(还没有运行完毕)
interrupt()打断线程如果被打断线程正在sleep,wait,join会导致被打断线程抛出InterruptedException,并清除打断标记;如果打断的正在运行线程,则会设置打断标记;park的线程被打断,也会设置打断标记
interrupted()static判断当前线程是否被打断会清除打断标记
currentThread()static获取当前正在执行的线程
sleep(long n)static让当前执行的线程休眠n毫秒,休眠时让出cpu的时间片给其它线程
yield()static提示线程调度器让出当前线程对cpu的使用主要是为了测试和调试

3.6 start与run

直接调用run方法跟调用普通方法一样

测试

@Slf4j
public class Test5 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.debug("runing");
        });
        thread.run();
        log.debug("主线程运行");
    }
}

结果

img

调用start

img

线程状态

/**
 * @author xc
 * @date 2023/4/30 19:48
 */
@Slf4j
public class Test6 {
    public static void main(String[] args) {
        Thread t = new Thread(()->log.debug("子线程运行"));
        log.debug("线程状态{}",t.getState());
        t.start();
        log.debug("线程状态{}",t.getState());
    }
}

img

不能调用两次start

img

3.7 sleep与yield

sleep

  • 调用sleep会让当前线程从Running进入Timed Waiting状态(阻塞)
/**
 * @author xc
 * @date 2023/4/30 19:54
 */
@Slf4j
public class Test7 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        thread.start();
        Thread.sleep(5000);
        log.debug("子线程状态{}",thread.getState());
    }
}

img

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

img

  • 睡眠结束后的线程未必会立刻得到执行
  • 建议用TimeUnit的sleep代替Thread的sleep来获得更好的可读性
TimeUnit.SECONDS.sleep(5);

yield(礼让)

  • 具体yield会让当前线程从Running进入Runnable就绪状态,然后调度执行其它线程
  • 具体的实现依赖于操作系统的任务调度器

线程优先级

  • 线程优先级提示调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它
  • 如果cpu比较忙,那么优先级高的线程会获得更多的时间片,但cpu闲时,优先级几乎没有用

案例-防止CPU占用100%

sleep实现

  • 在没有利用cpu来计算时,不要让while(true)空转浪费cpu,这时可以使用yield或sleep来让出cpu的使用权给其他程序
while(true){
	try{
        Thread.sleep(50);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
  • 可以用wait或条件变量达到类似的效果
  • 不同的是,后两种都需要加锁,并且需要相应的唤醒操作,一般适用于要进行同步的场景
  • sleep适用于无需锁同步的场景

3.8 join方法详解

为什么需要join

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

import lombok.extern.slf4j.Slf4j;

/**
 * @author xc
 * @date 2023/5/5 9:57
 */
@Slf4j
public class Test10 {
    static int r = 0;

    public static void main(String[] args) throws InterruptedException {
        log.debug("开始");
        Thread t = new Thread(()->{
            log.debug("开始");
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.debug("结束");
            r = 10;
        });
        t.start();
        log.debug("结果为:{}",r);
        log.debug("结束");
    }
}

分析

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

解决方法

  • 用sleep行不行,为什么?

    • 可以,但是你不知道一个线程要运行多长时间
  • 用join,加在t1.start()之后即可

    • 等待某个线程运行结束后才继续向下运行

应用之同步

以调用方角度来讲,如果

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

img

等待多个结果

问,下面代码cost大约多少秒?

import lombok.extern.slf4j.Slf4j;

import static java.lang.Thread.sleep;

/**
 * @author xc
 * @date 2023/5/5 9:57
 */
@Slf4j
public class Test10 {
    static int r1 = 0;
    static int r2 = 0;
    public static void main(String[] args) throws InterruptedException {
        test2();
    }
    private static void test2() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            r1 = 10;
        });
        Thread t2 = new Thread(() -> {
            try {
                sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            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);
    }
}

结果

img

有时效的join

等够时间

import lombok.extern.slf4j.Slf4j;

import static java.lang.Thread.sleep;

/**
 * @author xc
 * @date 2023/5/5 9:57
 */
@Slf4j
public class Test10 {
    static int r1 = 0;
    static int r2 = 0;
    public static void main(String[] args) throws InterruptedException {
        test2();
    }
    private static void test2() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            r1 = 10;
        });
        long start = System.currentTimeMillis();
        t1.start();
        t1.join(2000);
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
    }
}

结果

img

没等够时间

import lombok.extern.slf4j.Slf4j;

import static java.lang.Thread.sleep;

/**
 * @author xc
 * @date 2023/5/5 9:57
 */
@Slf4j
public class Test10 {
    static int r1 = 0;
    static int r2 = 0;
    public static void main(String[] args) throws InterruptedException {
        test2();
    }
    private static void test2() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            r1 = 10;
        });
        long start = System.currentTimeMillis();
        t1.start();
        t1.join(500);
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
    }
}

结果

img

3.9 interrupt方法详解

打断sleep,wait,join的线程

这几个方法都会让线程进入阻塞状态

打断sleep的线程,会清空打断状态,sleep为例

import lombok.extern.slf4j.Slf4j;

/**
 * @author xc
 * @date 2023/5/5 10:34
 */
@Slf4j
public class Test11 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t.start();
        Thread.sleep(1000);
        t.interrupt();
        log.debug("打断标记{}",t.isInterrupted());
    }
}

结果

img

打断正常运行的线程

import lombok.extern.slf4j.Slf4j;

/**
 * @author xc
 * @date 2023/5/5 10:34
 */
@Slf4j
public class Test11 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(()->{
            while (true) {
                boolean interrupted = Thread.currentThread().isInterrupted();
                if (interrupted) {
                    log.debug("被打断");
                    break;
                }
            }
        });
        t.start();
        Thread.sleep(1000);
        t.interrupt();
        log.debug("打断标记{}",t.isInterrupted());
    }
}

结果

img

两阶段终止模式

Two Phase Termination

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

1.错误思路

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

    • stop方法会真正杀死线程,如果这时线程锁住了共享资源,那么当它被杀死后就再也没有机会释放锁,其它下次你将永远无法获取锁
  • 使用System.exit(int) 方法停止线程

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

2.两阶段终止模式

img

import lombok.extern.slf4j.Slf4j;

/**
 * @author xc
 * @date 2023/5/5 11:02
 */
@Slf4j
public class Test12 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination t = new TwoPhaseTermination();
        t.start();
        Thread.sleep(10000);
        t.stop();
    }
}

@Slf4j
class TwoPhaseTermination{
    private Thread monitor;

    public void start(){
        monitor = new Thread(()->{
            while (true) {
                boolean interrupted = Thread.currentThread().isInterrupted();
                if (interrupted) {
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(2000);
                    log.debug("记录日志");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                }
            }
        });
        monitor.start();

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

结果

img

打断park线程

打断park线程,不会清空打断状态

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.LockSupport;

/**
 * @author xc
 * @date 2023/5/5 11:16
 */
@Slf4j
public class Test13 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            log.debug("park");
            LockSupport.park();
            log.debug("unpark...");
            log.debug("打断状态:{}",Thread.interrupted());

            LockSupport.park();
            log.debug("unpark...");
        });
        t.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
        t.interrupt();
    }
}

结果

img

3.10 不推荐的方法

方法名static功能说明
stop()停止线程运行
suspend()挂起(暂停)线程运行
resume()恢复线程运行

3.11 主线程与守护线程

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

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

注意:

  • 垃圾回收线程是一种守护线程
  • Tomcat中的Acceptor和Poller线程都是守护线程,所以Tomcat接受到shutdown命令后,不会等待他们处理完当前请求

3.12 五种状态

从操作系统层面来描述

img

  • 初始状态:仅是在语言层面创建了线程对象,还未与操作系统线程关联

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

  • 运行状态:获取了cpu时间片运行中的状态

    • 当cpu时间片用完,会从运行状态变成可运行状态,会导致线程上下文切换
  • 阻塞状态:

    • 如果调用阻塞API,如BIO读写文件,这时该线程实际不会用到cpu,会导致线程上下文切换,进入阻塞状态
    • 等BIO操作完毕,会由操作系统唤醒阻塞的线程,转换至可运行状态
    • 与可运行状态的区别是,对阻塞状态的线程来说只要 它们一直不唤醒,调度器就一直不会考虑调度它们
  • 终止状态:表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

3.13 六种状态

img

  • NEW 线程刚被创建,但是还没有调用 start() 方法
  • RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的 【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为 是可运行)
  • BLOCKED , WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分
  • TERMINATED 当线程代码运行结束

3.14 本章小结

  • 线程创建

  • 线程重要api,如start、run、sleep、join、interrupt等

  • 线程状态

  • 应用方面

    • 异步调用:主线程执行期间,其它线程异步执行耗时操作
    • 提高效率:并行计算,缩短运算时间
    • 同步等待:join
    • 统筹规划:合理使用线程,得到最优效果
  • 原理方面

    • 线程运行流程:栈、栈帧、上下文切换、程序计数器
    • Thread两种创建方式源码
  • 模式方面

作系统线程关联),可以由cpu调度执行

  • 运行状态:获取了cpu时间片运行中的状态

    • 当cpu时间片用完,会从运行状态变成可运行状态,会导致线程上下文切换
  • 阻塞状态:

    • 如果调用阻塞API,如BIO读写文件,这时该线程实际不会用到cpu,会导致线程上下文切换,进入阻塞状态
    • 等BIO操作完毕,会由操作系统唤醒阻塞的线程,转换至可运行状态
    • 与可运行状态的区别是,对阻塞状态的线程来说只要 它们一直不唤醒,调度器就一直不会考虑调度它们
  • 终止状态:表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态

3.13 六种状态

[外链图片转存中…(img-j8AwIAQE-1683460701414)]

  • NEW 线程刚被创建,但是还没有调用 start() 方法
  • RUNNABLE 当调用了 start() 方法之后,注意,Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的 【可运行状态】、【运行状态】和【阻塞状态】(由于 BIO 导致的线程阻塞,在 Java 里无法区分,仍然认为 是可运行)
  • BLOCKED , WAITING , TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分
  • TERMINATED 当线程代码运行结束

3.14 本章小结

  • 线程创建

  • 线程重要api,如start、run、sleep、join、interrupt等

  • 线程状态

  • 应用方面

    • 异步调用:主线程执行期间,其它线程异步执行耗时操作
    • 提高效率:并行计算,缩短运算时间
    • 同步等待:join
    • 统筹规划:合理使用线程,得到最优效果
  • 原理方面

    • 线程运行流程:栈、栈帧、上下文切换、程序计数器
    • Thread两种创建方式源码
  • 模式方面

    • 终止模式之两阶段终止

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

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

相关文章

OpenCV-Python图像几何变换

概念&#xff1a;几何变换是指将一幅图像映射到另外一幅图像内的操作。OpenCV提供了多个与映射有关的函数&#xff0c;这些函数使用起来方便灵活&#xff0c;能够高效地完成图像的映射。 缩放 cv2.resize()函数用于缩放图像 det cv2.resize(src, dsize[,dst[,fx[,fy[,interp…

入门级BP神经网络实现回归预测的原理与python代码

文章目录 一、BP神经网络是什么&#xff1f;二、BP神经网络的原理&#xff1f;三、BP神经网络预测过程如下1.初始化参数&#xff1a;2.前向传播&#xff1a;3.反向传播&#xff1a;4.更新参数&#xff1a;5.重复上述过程&#xff0c;直到满足收敛条件或达到最大迭代轮次。 四、…

【Liunx】进程地址空间

文章目录 &#x1f4d6; 前言1. 环境变量收尾1.1 认识bash进程&#xff1a;1.2 环境变量具有全局属性&#xff1a;1.3 内建命令&#xff1a; 2. 进程地址空间2.1 Liunx — 地址空间验证&#xff1a;2.2 感知地址空间的存在&#xff1a;2.3 认识地址空间&#xff1a;2.3 - 1&…

C++右值引用(左值表达式、右值表达式)(移动语义、完美转发(右值引用+std::forward))(有问题悬而未决)

文章目录 什么是右值&#xff1f;是什么是右值引用&#xff1f;什么是移动语义&#xff1f;什么是完美转发&#xff1f;&#xff08;右值引用std::forward&#xff09; 什么是右值&#xff1f; 在 C 中&#xff0c;表达式可以分为左值表达式和右值表达式。左值表达式指的是可以…

《常规脉搏传输时间作为人体血压变化标志》阅读笔记

目录 一、论文摘要 二、论文十问 Q1: 论文试图解决什么问题&#xff1f; Q2: 这是否是一个新的问题&#xff1f; Q3: 这篇文章要验证一个什么科学假设&#xff1f; Q4: 有哪些相关研究&#xff1f;如何归类&#xff1f;谁是这一课题在领域内值得关注的研究员&#xff1f; …

OpenCV教程——加载、修改、保存图像

1.颜色空间 颜色空间&#xff1a;也称彩色模型&#xff08;又称彩色空间或彩色系统&#xff09;。本质上&#xff0c;彩色模型是坐标系统和子空间的阐述。位于系统的每种颜色都有单个点表示。RGB&#xff08;红绿蓝&#xff09;是依据人眼识别的颜色定义出的空间&#xff0c;可…

「二线豪华」或成历史,理想反超沃尔沃再树「里程碑」

今年的上海车展&#xff0c;除了占据C位的新能源汽车&#xff0c;还有传统车企。 上海车展开幕前&#xff0c;沃尔沃汽车大中华区销售公司总裁钦培吉在新车发布会上直言&#xff1a;“新势力会的&#xff0c;我们三年就学会了&#xff1b;我们会的&#xff0c;新势力十年都学不…

SQL事务与存储引擎

索引回顾&#xff1a; 索引是一个排序的列表&#xff0c;包含字段的值和值所在行数据的物理地址 事务是一个机制&#xff0c;一个操作序列&#xff08;一组操作命令&#xff09;&#xff0c;事务会把所有命令当做一个整体向系统提交或撤销操作&#xff0c;要么都执行&#xf…

MySQL中的Join 的算法(NLJ、BNL、BKA)

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 文章目录 摘要什么是JoinIndex Nested-Loop JoinBlock Nested-Loop JoinMRR & BKA总结 摘要 Join是MySQL中最常见的查询操作之一&#xff0c;用于从多个表中获取数据并将它们组合在一起。Join算法通常使…

【概念大全(关系,码,选择,投影,连接,运算)】第二章 关系数据库

第二章 关系数据库 1. 关系的基本概念1. 什么是域2. 笛卡尔积3. 笛卡尔积中 有意义的子集 就是关系4. 候选码 &#xff08;是唯一标识符 并不是用 只有一个进行判断&#xff09;5. 全码&#xff08;一行中都不重复&#xff09;6. 主码&#xff08;候选码选一个就是主码&#xf…

手术麻醉临床信息系统源码,实时自动采集麻醉和监护设备的数据

手术麻醉临床信息系统源码 手术麻醉临床信息系统实时采集麻醉和监护设备的数据&#xff0c;实现术前、术中、术后全手术过程的数字化管理&#xff0c;为手术室提供全数字化的业务管理、临床管理、费用管理、材料管理等。同时通过与 HIS、EMR、PACS、LIS 等系统无缝集成&#x…

Linux中信号的基础知识

信号的概念 Linux操作系统中&#xff0c;信号是一种进程间通信&#xff08;Inter-Process Communication, IPC&#xff09;机制&#xff0c;用于向其他进程发送通知或指示&#xff0c;通常是为了通知特定事件的发生&#xff0c;如程序终止、用户按下特定按键等。信号提供了一种…

java获取输入内容的方法

Java中的对象类型可以有多种&#xff0c;比如 Object、 StringBuilder等&#xff0c;其中 Object和 String是最常用的对象类型&#xff0c;而 StringBuilder类是一种特殊的类&#xff0c;它能通过继承来创建其他的对象。 我们在平时的工作中经常会遇到需要获取输入内容的情况&a…

界面控件DevExpress Blazor UI v22.2亮点:全新的Window组件

DevExpress拥有.NET开发需要的所有平台控件&#xff0c;包含600多个UI控件、报表平台、DevExpress Dashboard eXpressApp 框架、适用于 Visual Studio的CodeRush等一系列辅助工具&#xff0c;该组件拥有众多新产品和数十个具有高影响力的功能&#xff0c;可为桌面、Web和移动应…

详解async 与 await,带您理解Playwright使用异步方法的正确姿势!

大家在使用python做playwright自动化测试的过程中&#xff0c;一定会发现下面这种异步用法 async def func():await apiawait api 很多同学可能只是按照这种写法来编写项目的自动化测试代码&#xff0c;对于具体细节可能并不了解&#xff0c;今天我就来讲一下playwright异步用…

基于fNIRS的脑功能连接分析:图论方法

导读 背景&#xff1a;fNIRS是一种利用近红外光谱进行功能神经成像的光学脑监测技术。它使用近红外光来测量大脑活动&#xff0c;并估计由于运动活动而引起的大脑皮层血流动力学活动。fNIRS通过光学吸收来测量含氧和脱氧血红蛋白中氧水平的变化。多源噪声和伪影干扰导致的信号…

【P6】JMeter HTTP Cookie管理器

文章目录 一、测试网站二、Cookie 设置规则2.1、无配置元件时&#xff0c;Cookie 不会自动设置&#xff08;与线程组设置无关&#xff09;2.2、有配置元件&#xff0c;不选任何参数时&#xff0c;Cookie 自动设置&#xff08;与线程组设置无关&#xff09;2.3、有配置元件&…

Java——二叉搜索树中第k小的元素

题目链接 leetcode在线oj题——二叉搜索树中第k小的元素 题目描述 给定一个二叉搜索树的根节点 root &#xff0c;和一个整数 k &#xff0c;请你设计一个算法查找其中第 k 个最小元素&#xff08;从 1 开始计数&#xff09;。 题目示例 示例1 输入&#xff1a;root [3,1…

软件工程本科生毕业论文中常见问题总结

文章目录 目录结构不合理 绪论&#xff08;引言&#xff09;研究内容 表格表格首行不要加粗表格能不跨页的就不要跨页 其他常见格式问题专有名词要用统一写法 首先先仔细阅读&#xff1a; 本科生毕业论文&#xff08;设计&#xff09;写作与排版打印规范 目录 结构不合理 2.…

Ubuntu 增加swap交换内存

一、创建虚拟内存 在实际开发中发现swap交换分区不够用了&#xff0c;于是需要创建虚拟内存来增加交换分区的大小。 在系统空闲空间位置创建swap虚拟内存专用文件夹 cd /data //切到你想要创建交换分区的目录 mkdir swap //新建文件夹swap cd swap //进入swap文件夹 备…