多线程(基础)

  前言👀~

上一章我们介绍了什么是进程,对于进程就了解那么多即可,我们作为java程序员更关注线程,线程内容比较多,所以我们要分好几部分才能讲完

目录

进程的缺点

 多线程(重要)

进程和线程的区别(经典面试题)

java如何进行多线程编程?

创建线程的方式(面试题)

继承Thread类(来自java.lang包下)

使用匿名内部类继承Thread类重写run

实现Runnable接口

使用匿名内部类实现Runnable接口重写run

使用lambda表达式创建线程(推荐)

Thread 类及常见方法

Thread类的构造方法

Thread类的常见属性

启动线程

中断线程

等待线程

查看线程状态


如果各位对文章的内容感兴趣的话,请点点小赞,关注一手不迷路,如果内容有什么问题的话,欢迎各位评论纠正 🤞🤞🤞

12b46cd836b7495695ce3560ea45749c.jpeg

个人主页:N_0050-CSDN博客

相关专栏:java SE_N_0050的博客-CSDN博客  java数据结构_N_0050的博客-CSDN博客  java EE_N_0050的博客-CSDN博客


进程的缺点

多进程编程的缺点:进程太重量效率不高,创建进程和销毁进程和调度进程消耗的时间都是比较多的(消耗在申请资源上),因为我们知道进程是系统资源分配的基本单元,所以在给进程分配资源的时候是一个大活。拿分配内存说,操作系统内部也有一定的数据结构,用来管理空闲的内存,当进程申请内存空间的时候,操作系统就会从这个数据结构中找到大小合适空闲的内存返回给进程。这里的数据结构可以提高一定的效率,但是总体来说和线程相比还是比较耗时的。同时频繁创建和销毁进程和进程切换是一个开销很大的操作,多进程和多线程都能实现并发编程,线程比进程更轻量。有些任务场景需要 “等待 IO”, 为了让等待 IO 的时间能够去做⼀些其他的工作, 也需要用到并发编程. 其次,虽然多进程也能实现 并发编程, 但是线程比进程更轻量


 多线程(重要)

进程想要执行任务就需要依赖线程线程不能独立存在,需要依附于进程(进程包含线程,进程可包含一个线程也可包含多个线程),一个进程在最开始的时候,至少要有一个线程(主线程),这个线程负责完成执行代码的工作,我们也可以根据需要创建多个线程,从而实现"并发编程"的效果换句话说,就是进程中的最小执行单位就是线程

线程也称轻量级线程(创建、销毁、调度都比进程快),每个线程就是一个独立的 "执行流"(因为在执行用户写的代码)可以独立的执行一些代码,每一个线程可以执行一系列的操作(也就是代码)

更好的理解线程,还是拿之前在进程举的演员的例子,一个舞台可以有多个演员,但是呢这个多个演员来自不同的剧组,这里面的演员就是线程,剧组就是进程。在我们之前谈的进程调度都是基于一个进程只有一个线程,可以理解为之前每个剧组都只有一个演员。实际上,一个进程可以有多个线程,每个线程都可以独立进行调度。之后谈到进程调度的话,不是调度整个进程,而是调度进程中的每一个线程。就比如说这个导演叫这个剧组的所有人来拍戏,所有的演员由导演进行分配角色、上场时间、台词等,相当于线程也有状态、优先级、上下文、记账信息

下面有图更好理解多线程
 
一个工厂代表一个进程,一个生产线代表一个线程,我们之前提到的多进程是下面这样子的 


下面这样是我们说的多线程,一个进程中有多个线程,同个工厂(共用资源)两个生产线(多线程)提高了效率


下面这么多人吃100个坤,适当的线程数目(里面的人)能提高效率,但是线程数目过多的情况,效率会降低并且造成线程冲突(线程不安全)


当人数过多且有人吃不到坤的时候,生气了把鸡全扔了,此时引发线程异常,如果我们没有处理好,可能会导致整个进程崩了,其他线程也会随之消失


总结:一个进程使用PCB来表示,一个进程可以使用一个PCB表示也可与使用多个PCB表示,每个TCB对应一个线程(可以理解PCB中包含TCB),并且每个线程都有这些信息(状态、优先级、上下文、记账信息,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈)辅助调度,除此之外,前面说到的属性pid是相同的,内存指针、文件描述符表也是共用一份的,根据这些信息我们可以得出线程的特点:每个线程都可以独立去cpu上调度执行、同一个线程的多个进程之间共用一份内存空间和文件资源,所以我们创建线程的时候不需要像进程一样申请资源(但是呢创建第一个线程的时候,相当于和进程一起创建的,所以我们要去申请资源,这里申请资源算在进程头上。后续再创建线程的话就是共享同一份了),我们直接用系统给进程分配好的资源,这样大大提高了我们的效率和节省开销。综上所述我们可以得出进程是资源分配的基本单位,线程是CPU 调度执行的基本单位

每个线程都是独立调度的,在调度的过程中,系统就不考虑 进程 这样的概念了。所以就是不同进程中的线程可以被轮番调度,每个线程只有获得 CPU 的使用权才能执行指令。所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得 CPU 的使用权,分别执行各自的任务。


进程和线程的区别(经典面试题)

1.线程比进程更轻量、高效,线程不需要申请资源,和同一进程共用一份,省去了申请资源的开销

2.同一个进程内线程和线程之间会有影响(线程不安全和线程异常),一个线程崩了可能导致其他线程受到影响最终导致进程崩了。进程和进程之间具有独立性

3.线程依附于进程,一个进程至少有一个线程(主线程),也可以有多个线程

4.线程是调度执行的基本单位,进程是资源分配的基本单位


java如何进行多线程编程?

线程是操作系统的概念,操作系统提供了一套API来操作线程,java对操作系统提供的API进行封装(跨平台),我们学java的只需要掌握java封装过后的这套API就可以操作线程了

进程和进程之间能并发执行实现并发编程,线程和线程之间也能实现并发执行实现并发编程,我们学java的更侧重线程之间的并发执行

创建线程的方式(面试题)

继承Thread类(来自java.lang包下)

创建Thread对象,我们就可以操作 操作系统内部的线程了。以及重写入口方法run(描述了该线程要执行的任务)

class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("我开始工作了");
        System.out.println("我结束工作了");
    }
}

public class test1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
        System.out.println("我是主线程");
    }
}

输出:不确定

再来看下面这段代码,猜一下输出顺序

class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("thread线程正在工作");
        }
    }
}

public class test1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.start();
        while (true) {
            System.out.println("主线程正在工作");
        }
    }
}

输出:交替输出


为什么呢?我们也不知道这两个线程是同时执行的还是交替执行的(同一个核心上执行,还是分别在两个核心上执行),所以我们统称并发(并行+并发),实现并发编程的效果,为什么要实现并发编程?(充分利用多核cpu的资源)。

注意:打印顺序不一定,操作系系统对于多个线程的调度顺序是不确定的,随机的。虽然有先后顺序但是谁先谁后我们是不确定的(重要),这个随机取决于操作系统对于线程调度的模块(调度器)的具体实现

接着再来看下面这段代码,猜一下输出顺序

class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("thread线程正在工作");
        }
    }
}

public class test1 {
    public static void main(String[] args) {
        Thread thread = new MyThread();
        thread.run();
        while (true) {
            System.out.println("主线程正在工作");
        }
    }
}

输出:thread线程正在工作   不只一条哈

原因:T.run 这种时候只有一个主线程,因为我们没有创建一个新的进程。等run这个方法结束后,才会执行后面的代码,相当于只有一个执行流,只能依次执行循环。相当于就是main线程在执行它的run方法,就跟我们在main方法中平常创建一个类然后调用方法一样,main线程在工作

不信的话代码拿去自己试试,然后用jdk中自带的工具jconsole,里面其他线程是JVM创建的

使用匿名内部类继承Thread类重写run

同样创建Thread对象,我们就可以操作 操作系统内部的线程了。以及重写入口方法run(描述了该线程要执行的任务)

public class test1 {
    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("我是匿名内部类");
            }
        };
        thread.start();
        System.out.println("我是主线程");
    }
}

实现Runnable接口

实现Runnable接口,它就表示的是一个可以运行的任务,所以还是需要创建线程来完成这个任务。这样理解我写了个任务,丢给线程去完成,但是我们要先把线程创建出来才能去完成

class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("我是Runnable接口");
        }
    }
}

public class test1 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        while (true) {
            System.out.println("主线程正在工作");
        }
    }
}

使用Runnable接口和继承Thread类的区别:主要是为了解耦合,使用Runnable接口相当于跟线程拆分开,把任务抽离出来,可以把这个任务丢给任意一个线程去完成,可以用在需要重复完成这个任务的场景,直接继承Thread类就不行,它更适合完成一次性的任务

还有关于创建一个线程的时候,有两个关键的操作。一个是明确线程要执行的任务,我们更关注任务本身,如果这个任务就只是执行一段简单的代码至于用什么方式实现这个任务没什么区别。如果遇到复杂的任务有些方式可能就完成不了,这时候我们需要用其他的方式去完成,这时候我们把任务提取出来,我们可以自己选择指定的方式去完成这个任务    。另外一个操作就是通过调用系统API创建出线程。

使用匿名内部类实现Runnable接口重写run

public class test1 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是匿名内部类");
            }
        }) {
        };
        thread.start();
        System.out.println("我是主线程");
    }
}

使用lambda表达式创建线程(推荐)

public class test1 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            System.out.println("我是lambda表达式");
        });
        thread.start();
        System.out.println("我是主线程");
    }
}

除了上面的方式之外,还有其他的方式,后续讲解,还有一个点需要清楚就是创建Thread类的时候并没有真正创建线程,只有在调用start方法的时候调用系统API去创建线程的时候才算认识


Thread 类及常见方法

Thread类的构造方法

在Thread类源码里有个构造方法可以设置线程的名字,其他的就没什么好介绍的了

public class test1 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("我是lambda表达式");
            }
        }, "我叫线程A");
        thread.start();
    }
}

为什么这里没有显示main线程呢?因为main线程执行完了,线程的入口方法执行完了,这个线程自然就销毁了。对于主线程来说,入口方法就是main方法,它调用系统API去创建完线程之后就执行完了。所以如果线程都执行完了,进程也就结束了但只要有一个线程还在执行,进程就不会结束
 


Thread类的常见属性


1.ID:线程的身份标识就是用来区分线程的,类似进程的pid,只不过这里的ID是java提供的,不是系统api提供的

2.getState()方法:获取线程状态,后面有讲到

3.isDaemon()方法判断是否为后台线程(守护线程),守护线程就是用来告诉JVM,我的这个线程不重要不需要等待它运行完才退出,让JVM喜欢什么时候退出就退出。前台线程(非守护线程)就是告诉JVM,这个线程没执行完成之前,你不能退出。默认情况一个线程是前台线程守护进程(后台进程),默认情况一个线程是前台线程我们可以通过setDameon()方法去设置,这么设置之后主线程执行完后,没有其他前台线程了,这个进程自然也就结束了

public class test1 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("我是lambda表达式");
            }
        }, "我叫线程A");
        thread.setDaemon(true);//设置thread线程为守护线程
        thread.start();
    }
}

4.isAlive()方法:Thread对象的生命周期要比系统内核中的线程更长,线程没了Thread对象还在,我们要以系统内核中的线程为主。所以我们使用isAlive()进行判断,判断系统内核中的线程有没有结束,结束返回false,没结束返回true。简单的理解为 run 方法是否运行结束了

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("我是lambda表达式");
        }, "我叫线程A");
        System.out.println(thread.isAlive());
        thread.start();
        System.out.println(thread.isAlive());
        Thread.sleep(2000);
        System.out.println(thread.isAlive());
    }
}

输出:false true 我是lambda表达式 false

5.isInterrupted()方法:用来判断线程是否被中断,怎么判断呢?Thread内部有一个标志位进行判断。下面会讲到


启动线程

使用start()方法创建线程

start方法和run方法的区别:非常直白的说,你可以把run看作是任务,start是叫人过来完成这个任务的。专业点说就是start方法通过调用系统API在系统内核中创建线程然后执行run方法中的代码。run方法会在线程创建好后自动被被调用


中断线程

中断一个线程(终止/打断),让一个线程停止运行(销毁),在java中销毁/终止一个线程做法比较唯一(这个唯一不是说只有一个方法,而是说销毁一个进程是让run方法快点执行完),让run方法快点执行完。在C++中是可以直接强制终止一个线程的运行,就比如你打开一个文本编辑器输入一段信息,输入一半直接给你干没了

先补充两个方法currentThread()方法和sleep()方法后面要用到

获取当前线程引用:currentThread()方法返回当前线程对象的引用,哪个线程调用这个方法就获取哪个线程对象的引用

休眠当前线程:sleep()方法让线程睡觉的,你可以设置睡多久,然后时间到了系统把它叫醒(阻塞->就绪),注意睡醒之后不会马上回到cpu上运行,要排队,这里会涉及调度所以会有一定的开销。就是你睡醒了需要一点时间缓缓才能去工作。

interrupt()方法:使用interrupt()方法设置标志位,前面说了Thread内部有一个标志位用来判断线程是否结束,调用这个方法就把这个标志位设置成true。这样即使我们还在执行sleep方法,它也会被强制唤醒。

来猜猜下面执行代码线程会是什么状态以及输出什么

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(3000);
        thread.interrupt();
    }
}

答案是输出几个hello后报异常,然后说说过程,这个线程先执行sleep方法,然后这个线程处于睡眠,然后你使用了interrupt()方法设置把标志位为true,把这个线程唤醒了,那么这个线程就会继续工作,除非你不让它睡眠并且设置标志位为true。举个例子本来你在上班,然后突然有点困了,你同事让你睡会,结果领导来了你同事赶紧把你叫醒,你立马起来了。

使用interrupt()方法搭配sleep方法这样设置的标志位就像没效果一样,没有把你的线程中断,为什么这样设定呢?java期望线程收到中断的信息的时候,我们能够自己决定接下来要怎么处理,这样可以让我们在开发中有更多的操作空间,前提是通过异常的方式去唤醒。比如你打游戏女朋友叫你陪她去逛街,你可以直接关掉游戏陪她去,也可以说等我打完这把,还可以当个聋子啥也没听见。

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hello");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
//            第一种   System.out.println("一直工作");
//                    
//            第二种   System.out.println("工作一会休息了");
//                    break;
//                    
//            第三种   break;

                }
            }
        });
        thread.start();
        Thread.sleep(3000);
        thread.interrupt();
    }
}

等待线程

使用join()方法,让一个线程等待另外一个线程执行结束再执行。前面说线程并发执行的时候,执行的顺序是不确定的随机的,此时我们可以通过这个方法来控制线程结束的顺序。并且我们可以设置等待时间,如果没有设置等待时间,默认的话这个线程会一直等到那个线程执行结束后才会执行自己的任务,类似舔狗有一直舔的也有舔一段时间不舔了

join方法的工作过程:

1.如果主线程正在运行,主线程中调用了A.join方法,此时主线程进入阻塞状态,A线程执行完,主线程才会解除完成接下来的任务

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("我先干完活,你再干");
            }
        });
        thread.start();
        System.out.println("我要干活了");
        thread.join();
        System.out.println("轮到我干活了");
    }
}

输出结果

2.如果主线程任务已经执行完了,再调用A.join方法,就不用进入阻塞状态了,直接结束了

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("我先干完活,你再干");
            }
        });
        thread.start();
        System.out.println("我马上干完不给你机会干");
        thread.join();
    }
}

输出结果


查看线程状态

和进程一样,线程也有运行、就绪、阻塞状态。真正在系统调度的还是线程,线程是调度执行的基本单位

在java中,给线程赋予了一些其他的状态:

1.NEW:Thread对象已经存在了,但是系统线程还没创建,也就是还没调用start方法

2.RUNNABLE:就绪状态,这里有两种表示,一种是在cpu上执行了,另一种是正等着去cpu上执行

3.TIMED_WATING:阻塞,被sleep这种固定时间的方式打断产生的阻塞

4.WATING:阻塞,被wait这种不固定时间的方式打断产生的阻塞

5.BLOCKED:阻塞,等待锁导致的阻塞,后续死锁讲解

6.TERMINATED:Thread对象还在,操作系统内核的线程结束了,也这样理解意味着该线程已经完成了其run方法的执行

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("java");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        System.out.println(t.getState());//只创建了Thread对象的NEW状态
        t.start();
        System.out.println(t.getState());//创建完线程t就是这个RUNNABLE状态

        Thread.sleep(1000);
        System.out.println(t.getState());//t线程处于睡眠

        t.interrupt();
        Thread.sleep(1000);
        System.out.println(t.getState());//t线程执行完了
    }
}

输出结果

后续线程出现卡死的情况,我们可以通过上述后面三种状态去确定卡死的原因是什么,最后两个状态后续再讲解

上述方法是多线程中非常常用的方法,要好好掌握,今天的内容就先到这,后面我们接着讲解线程还有不少的知识点等着💕

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据分析必备:一步步教你如何用matplotlib做数据可视化(14)

1、Matplotlib 图像 Matplotlib包中的图像模块提供加载&#xff0c;重新缩放和显示图像所需的功能。Pillow库支持加载图像数据。Matplotlib仅支持PNG图像。如果本机读取失败&#xff0c;下面显示的命令将回退到Pillow。 此示例中使用的图像是PNG文件&#xff0c;但请记住数据的…

计算机网络之奇偶校验码和CRC冗余校验码

今天我们来看看有关于计算机网络的知识——奇偶校验码和CRC冗余校验码&#xff0c;这两种检测编码的方式相信大家在计算机组成原理当中也有所耳闻&#xff0c;所以今天我就来跟大家分享有关他们的知识。 奇偶校验码 奇偶校验码是通过增加冗余位使得码字中1的个数恒为奇数或偶数…

JS在线加密简述

JS在线加密&#xff0c;是指&#xff1a;在线进行JS代码混淆加密。通过混淆、压缩、加密等手段&#xff0c;使得JS源代码难以阅读和理解。从而可以有效防止代码被盗用或抄袭&#xff0c;保护开发者的知识产权和劳动成果。常用的JS在线加密网站有&#xff1a;JShaman、JS-Obfusc…

AI写作助力:如何用AI降重工具快速提升论文原创性?

高查重率是许多毕业生的困扰。通常&#xff0c;高查重率源于过度引用未经修改的参考资料和格式错误。传统的降重方法&#xff0c;如修改文本和增添原创内容&#xff0c;虽必要但耗时且成效不一。 鉴于此&#xff0c;应用AI工具进行AIGC降重成为了一个高效的解决方案。这些工具…

线性相关,无关?秩?唯一解(只有零解),无穷解(有非零解)?D=0,D≠0?

目录 线性有关无关 和 唯一解&#xff08;只有零解&#xff09;&#xff0c;无穷解&#xff08;有非零解&#xff09;之间的关系 D0&#xff0c;D≠0&#xff1f; 和 秩 的关系 串起来&#xff1a; 线性相关&#xff0c;无关&#xff1f;秩&#xff1f;唯一解&#xff08;只…

cuda 学习笔记4

一 基本函数 在GPU上开辟空间&#xff0c;无论定义的数据是float还是int ,还是****gpu_int,分配空间的函数都是下面固定的形式 (void**)& 1.函数定义&#xff0c;global void 是配套使用的&#xff0c;是在GPU上定义&#xff0c;也就是GPU上执行&#xff0c;CPU上调用的函数…

【软件测试】白盒测试与接口测试详解

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一、什么是白盒测试 白盒测试是一种测试策略&#xff0c;这种策略允许我们检查程序的内部结构&a…

短视频带货实战营(高阶课),从0到1做个赚钱的抖音号(17节课)

课程目录&#xff1a; 1-短视频带贷先导课_1.mp4 2-账号搭建_1.mp4 3-账号养号涨粉套路_1.mp4 4-开通橱窗_1.mp4 5-管家式选品_1.mp4 6-六个能赚钱的赛道_1.mp4 7-选品之精选联盟_1.mp4 8-好物分享的三种形式_1.mp4 9-短视频之图文课_1.mp4 10-短视频之剪辑课_1.mp4 …

el-upload+python fastAPI实现上传文件

el-upload通过action指定后端接口&#xff0c;并通过name指定传输的文件包裹在什么变量名中 <el-uploadclass"upload-demo"dragaction"https://ai.zscampus.com/toy/upload"multiplename"fileList":limit"10"accept".xlsx, .x…

昇思25天学习打卡营第9天|使用静态图加速

一、简介&#xff1a; AI编译框架分为两种运行模式&#xff0c;分别是动态图模式以及静态图模式。MindSpore默认情况下是以动态图模式运行&#xff0c;但也支持手工切换为静态图模式。两种运行模式的详细介绍如下&#xff1a; &#xff08;1&#xff09;动态图&#xff1a; …

如何使用Hugging Face Transformers为情绪分析微调BERT?

情绪分析指用于判断文本中表达的情绪的自然语言处理(NLP)技术&#xff0c;它是客户反馈评估、社交媒体情绪跟踪和市场研究等现代应用背后的一项重要技术。情绪可以帮助企业及其他组织评估公众意见、提供改进的客户服务&#xff0c;并丰富产品或服务。 BERT的全称是来自Transfo…

The First Descendant第一后裔联机失败、联机报错这样处理

第一后裔/The First Descendant是一款免费的多人合作射击游戏&#xff0c;玩家将进入一片混乱的英格里斯大陆&#xff0c;扮演继承者后裔&#xff0c;通过各种主支线任务和故事剧情触发&#xff0c;最终揭开自身的秘密&#xff0c;并带领大家一起抵抗邪恶势力的入侵。为了避免玩…

【Java Web】三大域对象

目录 一、域对象概述 二、三大域对象 三、域对象使用相关API 一、域对象概述 一些可用于存储数据和传递数据的对象被称为域对象&#xff0c;根据传递数据范围的不同&#xff0c;我们称之为不同的域&#xff0c;不同的域对象代表不同的域&#xff0c;共享数据的范围也不同。 二、…

ISP IC/FPGA设计-第一部分-SC130GS摄像头分析-IIC通信(1)

1.摄像头模组 SC130GS通过一个引脚&#xff08;SPI_I2C_MODE&#xff09;选择使用IIC或SPI配置接口&#xff0c;通过查看摄像头模组的原理图&#xff0c;可知是使用IIC接口&#xff1b; 通过手册可知IIC设备地址通过一个引脚控制&#xff0c;查看摄像头模组的原理图&#xff…

数据库调优厂商 OtterTune 宣布停止运营

昨天刷到消息&#xff0c;得知数据库优化厂商 OtterTune 停止了运营。OtterTune 的成员主要来自 CMU Andy Pavlo 教授领导的数据库实验室。公司正式成立于 2021 年 5 月&#xff0c;融资了 1450 万美金。 按照 Andy 教授的说法&#xff0c;公司是被一个收购 offer 搞砸了。同时…

npm-check【实用教程】升级项目中的依赖

安装 npm-check npm i -g npm-check检查项目中的依赖 npm-check会显示项目中没有使用&#xff0c;以及有新版本的依赖 升级项目中的依赖 npm-check -u方向键上下可以移动图中左侧的箭头空格键可选中/取消选中标注为 Major Update 和 Non-semver 类的版本&#xff0c;需去官网查…

用MySQL和navicatpremium做一个项目—(财务管理系统)。

1 ER图缩小的话怕你们看不清&#xff0c;所以截了两张图 2 vsdx绘图结果 3DDL和DML,都有点长分了好多次上传&#xff0c;慢慢看 DDL -- 用户表 CREATE TABLE users (user_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 用户ID,username VARCHAR(50) NOT NULL UNIQUE COMMENT 用…

奔驰汽车的通信如此固若金汤的原因

随着摄像系统、距离控制、航线保持等功能以及制动辅助系统、制动力分配系统、车身侧倾干预与缓解系统等功能的飞速发展,汽车的系统功能之间已经不再独立,而是呈现互相合作的关系,各功能之间的无缝集成更是各大整车厂追求的目标。俗话说,外练筋骨皮,内练一口气,有了各式安…

第4章 客户端-客户端案例分析

1 Redis内存陡增 1.1.现象 服务端现象&#xff1a;Redis主节点内存陡增&#xff0c;几乎用满maxmemory&#xff0c;而从节点内存并没有变化&#xff08;正常情况下主从节点内存使用量基本相同&#xff09;。 客户端现象&#xff1a;客户端产生了OOM异常&#xff0c;也就是Redis…

桃园三结义 | 第1集 | 三人一条心,黄土变成金,有你带着俺,大事定能成功啊!| 正所谓择木之禽,得其良木,择主之臣,得遇明主 | 三国演义 | 群雄逐鹿

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;这篇博客是毛毛张结合三国演义原著分享三国演义文学剧本中的经典台词和语句&#xff0c;本篇分享的是《三国演义》第Ⅰ部分《群雄逐鹿》的第1️⃣集《桃…