线程的基本概念

news2025/7/6 1:23:14

文章目录

  • 基础概念
    • 线程与进程
      • 什么是进程?
      • 什么是线程?
      • 进程和线程的区别:
    • 多线程
      • 什么是多线程?
      • 多线程的局限性
    • 串行、并行、并发
    • 同步异步、阻塞非阻塞
  • 线程的创建
    • 1、继承Thread类,重写run方法
    • 2、实现Runnable接口,重写run方法
    • 3、实现Callable,重写call方法,配合FutureTask
    • 4、基于线程池构建线程 ExecutorService
  • 线程的使用
    • 线程的状态
    • 线程的常用方法
      • 获取当前线程Thread.currentThread()
      • 线程的名字getName()
      • 线程的优先级Thread.getPriority()
      • 线程的让步Thread.yield()
      • 线程的休眠Thread.sleep()
      • 线程的强占Thread.join()
      • 守护线程Daemon
      • 线程的等待和唤醒 Object.wait()&Object.notify()
    • 线程的结束方式
      • stop方法(不用)
      • 使用共享变量(很少用)
      • interrupt方式
    • wait和sleep的区别?

基础概念

线程与进程

什么是进程?

进程是指运行中的程序。 比如我们使用钉钉浏、览器等,启动这个程序时操作系统会给这个程序分配一定的资源(占用内存资源)。

什么是线程?

线程是CPU调度的基本单位,每个线程执行的都是某一个进程的代码的某个片段。
所谓进程就是线程的容器,需要线程利用进程中的一些资源,处理一个代码、指令,最终实现进程所预期的结果。

进程和线程的区别:

  • 根本不同:进程是操作系统分配的资源,而线程是CPU调度的基本单位。
  • 资源方面:同一个进程下的线程共享进程中的一些资源。线程同时拥有自身的独立存储空间。进程之间的资源通常是独立的。
  • 数量不同:进程一般指的就是一个进程。而线程是依附于某个进程的,而且一个进程中至少会有一个或多个线程。
  • 开销不同:毕竟进程和线程不是一个级别的内容,线程的创建和终止的时间是比较短的。而且线程之间的切换比进程之间的切换速度要快很多。而且进程之间的通讯很麻烦,一般要借助内核才可以实现,而线程之间通讯相当方面。
  • … …

多线程

什么是多线程?

多线程是指:单个进程下同时运行的多个线程。
多线程的目的是为了提高CPU的利用率。
可以通过避免一些网络IO或者磁盘IO等需要等待的操作,让CPU去调度其他线程。这样可以大幅度的提升程序的效率,提高用户的体验。比如Tomcat可以做并行处理,提升处理的效率,而不是一个一个排队。比如要处理一个网络等待的操作,开启一个线程去处理需要网络等待的任务,让当前业务线程可以继续往下执行逻辑,效率是可以得到大幅度提升的。

多线程的局限性

  • 如果线程数量特别多,CPU在切换线程上下文时,会额外造成很大的消耗。
  • 任务的拆分需要依赖业务场景,有一些异构化的任务,很难对任务拆分,还有很多业务并不是多线程处理更好。
  • 线程安全问题:虽然多线程带来了一定的性能提升,但是再做一些操作时,多线程如果操作临界资源,可能会发生一些数据不一致的安全问题,甚至涉及到锁操作时,会造成死锁问题。

串行、并行、并发

串行就是一个一个排队,第一个做完,第二个才能上。
并行就是同时处理。
这里的并发并不是三高中的高并发问题,这里是多线程中的并发概念(CPU调度线程的概念)。 CPU在极短的时间内,反复切换执行不同的线程,看似好像是并行,但是只是CPU高速的切换。

并行囊括并发。并行就是多核CPU同时调度多个线程,是真正的多个线程同时执行。单核CPU无法实现并行效果,单核CPU是并发。

同步异步、阻塞非阻塞

同步与异步:执行某个功能后,被调用者是否会主动反馈信息
阻塞和非阻塞:执行某个功能后,调用者是否需要一直等待结果的反馈。
两个概念看似相似,但是侧重点是完全不一样的。

同步阻塞:比如用锅烧水,水开后,不会主动通知你。烧水开始执行后,需要一直等待水烧开。
同步非阻塞:比如用锅烧水,水开后,不会主动通知你。烧水开始执行后,不需要一直等待水烧开,可以去执行其他功能,但是需要时不时的查看水开了没。
异步阻塞:比如用水壶烧水,水开后,会主动通知你水烧开了。烧水开始执行后,需要一直等待水烧开。
异步非阻塞:比如用水壶烧水,水开后,会主动通知你水烧开了。烧水开始执行后,不需要一直等待水烧开,可以去执行其他功能。

异步非阻塞这个效果是最好的,平时开发时,提升效率最好的方式就是采用异步非阻塞的方式处理一些多线程的任务。

线程的创建

创建线程的几种方式:

1、继承Thread类,重写run方法

启动线程是调用start方法,这样会创建一个新的线程,并执行线程的任务。
如果直接调用run方法,这样会让当前线程执行run方法中的业务逻辑。

static class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Hello MyThread!");
    }
}

2、实现Runnable接口,重写run方法

static class MyRun implements Runnable {
    @Override
    public void run() {
        System.out.println("Hello MyRun!");
    }
}
// 最常用的两种方式:
public void myRunMethod(){
    // 匿名内部类方式:
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("MyRunnable!");
        }
    });
    // lamdba方式:
    Thread thread2 = new Thread(()->{
            System.out.println("MyRunnable!");
    });
}

3、实现Callable,重写call方法,配合FutureTask

Callable一般用于有返回结果的非阻塞的执行方法。同步非阻塞。

static class MyCall implements Callable<String> {
    @Override
    public String call() throws Exception {
        System.out.println("Hello MyCall!");
        return "success";
    }
}

4、基于线程池构建线程 ExecutorService

public class HowToCreateThread {
    // 启动线程的几种方式
    public static void main(String[] args) {
        new MyThread().start();

        new Thread(new MyRun()).start();

        new Thread(() -> {
            System.out.println("Hello Lambda!");
        }).start();

        Thread t = new Thread(new FutureTask<String>(new MyCall()));
        t.start();

        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(() -> {
            System.out.println("Hello ThreadPool!");
        });
        service.shutdown();
    }
}

注:追其底层,其实只有一种,实现Runnable。

线程的使用

线程的状态

网上对线程状态的描述有很多,有5种、6种、7种,都可以接受。
5种状态一般针对传统的线程状态来说的(操作系统层面的)
线程的五种状态

Java中给线程定义的是6种状态。
线程的6种状态

  • NEW:Thread对象被创建出来了,但是还没有执行start方法。
  • RUNNABLE: Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度)
  • BLOCKED:synchronized没有拿到同步锁,被阻塞的情况。
  • WAITING:调用wait方法就会处于WAITING状态,需要被手动唤醒。
  • TIME_WAITING:调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒。
  • TERMINATED:run方法执行完毕,线程生命周期结束。

BLOCKED、WAITING、TIME_WAITING都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程。

下面在Java代码中验证一下具体效果:

public static void main(String[] args) throws InterruptedException {
    // NEW
    Thread t1 = new Thread();
    System.out.println(t1.getState());

    // RUNNABLE
    Thread t2 = new Thread(() -> {
        while (true) {
        }
    });
    t2.start();
    Thread.sleep(500);
    System.out.println(t2.getState());

    // BLOCKED
    Object obj3 = new Object();
    Thread t3 = new Thread(() -> {
        // t3线程拿不到锁资源,导致变为BLOCKED状态
        synchronized (obj3) {
        }
    });
    // main线程拿到obj的锁资源
    synchronized (obj3) {
        t3.start();
        Thread.sleep(500);
        System.out.println(t3.getState());
    }

    // WAITING
    Object obj4 = new Object();
    Thread t4 = new Thread(() -> {
        synchronized (obj4) {
            try {
                obj4.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t4.start();
    Thread.sleep(500);
    System.out.println(t4.getState());

    // TIMED_WAITING
    Thread t5 = new Thread(() -> {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    t5.start();
    Thread.sleep(500);
    System.out.println(t5.getState());

    // TERMINATED
    Thread t6 = new Thread(() -> {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    t6.start();
    Thread.sleep(1000);
    System.out.println(t6.getState());
}

线程的常用方法

获取当前线程Thread.currentThread()

Thread的静态方法获取当前线程对象。

public static void main(String[] args) {
    // 获取当前线程的方法
    Thread main = Thread.currentThread();
    System.out.println(main);
    // Thread[#1,main,5,main]
    // "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]";
}

线程的名字getName()

在构建Thread对象完毕后,一定要设置一个有意义的名称,方面后期排查错误。

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        System.out.println(Thread.currentThread().getName());
    });
    t1.setName("模块-功能-计数器");
    t1.start();
}

线程的优先级Thread.getPriority()

其实就是CPU调度线程的优先级。 Java中给线程设置的优先级别有10个级别,从1~10任取一个整数。 如果超出这个范围,会排除参数异常的错误。

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 100; i++) {
            System.out.println("t1:"+i);
        }
    });
    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 200; i++) {
            System.out.println("t2:"+i);
        }
    });
    t1.setPriority(1);
    t2.setPriority(10);
    t2.start();
    t1.start();
}

线程的让步Thread.yield()

可以通过Thread的静态方法yield,让当前线程从运行状态转变为就绪状态。

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 100; i++) {
            if (i == 50) {
                Thread.yield();
            }
            System.out.println("t1:" + i);
        }
    });
    Thread t2 = new Thread(() -> {
        for (int i = 0; i < 100; i++) {
            System.out.println("t2:" + i);
        }
    });
    t2.start();
    t1.start();
}

线程的休眠Thread.sleep()

Thread的静态方法,让线程从运行状态转变为等待状态。

sleep有两个方法重载:

  • 第一个就是native修饰的,让线程转为等待状态的效果
  • 第二个是可以传入毫秒和一个纳秒的方法(如果纳秒值大于等于0.5毫秒,就给休眠的毫秒值 +1。如果传入的毫秒值是0,纳秒值不为0,就休眠1毫秒)

sleep会抛出一个InterruptedException

public static void main(String[] args) throws InterruptedException {
    System.out.println(System.currentTimeMillis());
    Thread.sleep(1000);
    System.out.println(System.currentTimeMillis());
}

线程的强占Thread.join()

Thread的非静态方法join方法。需要在某一个线程下去调用这个方法。
如果在main线程中调用了t1.join(),那么main线程会进入到等待状态,需要等待t1线程全部执行完毕,再恢复到就绪状态等待CPU调度。
如果在main线程中调用了t1.join(2000),那么main线程会进入到等待状态,需要等待t1执行2s后, 再恢复到就绪状态等待CPU调度。如果在等待期间,t1已经结束了,那么main线程自动变为就绪状态等待CPU调度。

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println("t1:" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t1.start();
    for (int i = 0; i < 10; i++) {
        System.out.println("main:" + i);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (i == 1) {
            try {
                t1.join(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

守护线程Daemon

默认情况下,线程都是非守护线程。
JVM会在程序中没有非守护线程时,结束掉当前JVM。
主线程默认是非守护线程,如果主线程执行结束,需要查看当前JVM内是否还有非守护线程,如果没有JVM直接停止。

public static void main(String[] args) {
    Thread t1 = new Thread(() -> {
        for (int i = 0; i < 10; i++) {
            System.out.println("t1:" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t1.setDaemon(true);
    t1.start();
}

线程的等待和唤醒 Object.wait()&Object.notify()

可以让获取synchronized锁资源的线程通过wait方法进去到锁的等待池,并且会释放锁资源。
可以让获取synchronized锁资源的线程,通过notify或者notifyAll方法,将等待池中的线程唤醒,添加到锁池中。
notify随机的唤醒等待池中的一个线程到锁池。
notifyAll将等待池中的全部线程都唤醒,并且添加到锁池。
在调用wait方法和notify以及norifyAll方法时,必须在synchronized修饰的代码块或者方法内部才可以,因为要操作基于某个对象的锁的信息维护。

public class MiTest {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            sync();
        }, "t1");
        Thread t2 = new Thread(() -> {
            sync();
        }, "t2");
        t1.start();
        t2.start();
        Thread.sleep(12000);
        synchronized (MiTest.class) {
            MiTest.class.notifyAll();
        }
    }

    public static synchronized void sync() {
        try {
            for (int i = 0; i < 10; i++) {
                if (i == 5) {
                    MiTest.class.wait();
                }
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

线程的结束方式

线程结束方式很多,最常用就是让线程的run方法结束。无论是return结束,还是抛出异常结束都可以。

stop方法(不用)

强制让线程结束,无论你在干嘛,不推荐使用。但是,他确实可以把线程干掉。

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
    t1.start();
    Thread.sleep(500);
    t1.stop();
    System.out.println(t1.getState());
}

使用共享变量(很少用)

这种方式用的也不多,有的线程可能会通过死循环来保证一直运行。
可以通过修改共享变量在破坏死循环,让线程退出循环,结束run方法。

static volatile boolean flag = true;

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        while (flag) {
            // 处理任务
        }
        System.out.println("任务结束");
    });
    t1.start();
    Thread.sleep(500);
    flag = false;
}

interrupt方式

共享变量方式。

public static void main(String[] args) throws InterruptedException {
    // 线程默认情况下, interrupt标记位:false
    System.out.println(Thread.currentThread().isInterrupted());
    // 执行interrupt之后,再次查看打断信息
    Thread.currentThread().interrupt();
    // interrupt标记位:ture
    System.out.println(Thread.currentThread().isInterrupted());
    // 返回当前线程,并归位为false interrupt标记位:ture
    System.out.println(Thread.interrupted());
    // 已经归位了
    System.out.println(Thread.interrupted());
    // =====================================================
    Thread t1 = new Thread(() -> {
        while (!Thread.currentThread().isInterrupted()) { 
            // 处理业务
        }
        System.out.println("t1结束");
    });
    t1.start();
    Thread.sleep(500);
    t1.interrupt();
}

通过打断WAITING或者TIMED_WAITING状态的线程,从而抛出异常自行处理。这种停止线程方式是最常用的一种,在框架和JUC中也是最常见的。

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        while (true) {
            // 获取任务
            // 拿到任务,执行任务
            // 没有任务了,让线程休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("基于打断形式结束当前线程");
                return;
            }
        }
    });
    t1.start();
    Thread.sleep(500);
    t1.interrupt();
}

wait和sleep的区别?

  • 单词不一样
  • sleep属于Thread类中的static方法,wait属于Object类的方法。
  • sleep属于TIMED_WAITING,自动唤醒;wait属于WAITING,需要手动唤醒。
  • sleep方法在持有锁时,执行,不会释放资源。wait在执行后,会释放锁资源。
  • sleep方法可以在持有锁或者不持有锁执行。wait方法必须在持有锁的时才能执行。

wait方法会将持有锁的线程从owner扔到waitSet集合中,这个操作是在修改ObjectMointor对象,如果没有持有synchronized锁的话,是无法操作ObjectMointor的。

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

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

相关文章

Tomcat的类加载机制

不遵循双亲委托 在JVM中并不是一次性地把所有的文件都加载到&#xff0c;而是按需加载&#xff0c;加载机制采用 双亲委托原则&#xff0c;如下图所示&#xff1a; BootStrapClassLoader 引导类加载器ExtClassLoader 扩展类加载器AppClassLoader 应用类加载器CustomClassLoad…

位姿图优化(CeresG2OGTSAM)

0. 简介 作为SLAM中常用的方法&#xff0c;其原因是因为SLAM观测不只考虑到当前帧的情况&#xff0c;而需要加入之前状态量的观测。就比如一个在二维平面上移动的机器人&#xff0c;机器人可以使用一组传感器&#xff0c;例如车轮里程计或激光测距仪。从这些原始测量值中&…

Python用selenium实现自动登录和下单的脚本

前言 学python对selenium应该不陌生吧 Selenium 是最广泛使用的开源 Web UI&#xff08;用户界面&#xff09;自动化测试套件之一。Selenium 支持的语言包括C#&#xff0c;Java&#xff0c;Perl&#xff0c;PHP&#xff0c;Python 和 Ruby。目前&#xff0c;Selenium Web 驱动…

打游戏哪种蓝牙耳机比较好?适合玩游戏的无线蓝牙耳机

2023年耳机市场一如既往地卷&#xff0c;不只是卷音质&#xff0c;还在外观和功能上做了许多的改进&#xff0c;以至于现在哪怕不懂耳机的人从各电商平台都能闭眼入一个款平价品牌耳机且极少会踩雷&#xff0c;玩游戏是当前年轻人的娱乐方式&#xff0c;下面整理了几款适合玩游…

Git push报错DeployKey does not support push code

错误描述用Git从本地仓库上传服务器仓库报错&#xff1a;DeployKey does not support push code错误代码&#xff1a;(通过$ git push origin master命令从本地仓库上传到服务器仓库)错误原因&#xff1a;没有注册ssh公钥解决办法&#xff1a;添加ssh公钥&#xff1a;先生成对应…

滤波算法 | 无迹卡尔曼滤波(UKF)算法及其MATLAB实现

目录简介UKF滤波滤波流程和公式MATLAB程序结论简介 本文接着分享位姿跟踪和滤波算法中用到的一些常用程序&#xff0c;希望为后来者减少一些基础性内容的工作时间。以往分享总结见文章&#xff1a;位姿跟踪 | 相关内容目录和链接总结&#xff08;不断更新中~~~&#xff09; 本…

代码随想录算法训练营第六天 |哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集 、202. 快乐数、 1. 两数之和

打卡第六天&#xff0c;补昨天的卡 今日任务 哈希表理论基础242.有效的字母异位词349.两个数组的交集202.快乐数1.两数之和 哈希表理论基础 哈希表是根据关键码的值而直接进行访问的数据结构。 哈希表能解决什么问题呢? 一般哈希表都是用来快速判断一个元素是否出现集合里。 …

JavaDoc生成API文档(powernode document)(内含源代码和导出的文档)

JavaDoc生成API文档&#xff08;powernode document&#xff09;&#xff08;内含源代码和导出的文档&#xff09; 源代码和导出的文档下载链接地址&#xff1a;https://download.csdn.net/download/weixin_46411355/87473296 目录JavaDoc生成API文档&#xff08;powernode do…

恺望数据:解决智驾数据生产痛点,提供自动化生产线和规模化人力

最近Chat GPT引起了一个热点话题&#xff0c;就是人工智能是否真的可以替代人类工作&#xff0c;特别是在需要进行数据标注等需要人力的领域。 自动驾驶数据服务公司恺望数据在最近的一个会议上透露了一些消息&#xff0c;他们已经推出了一个基于自动化的数据生产系统&#xff…

linux下安装minio

获取 MinIO 下载 URL:访问&#xff1a;https://docs.min.io/ 一&#xff0c;进入/opt 目录&#xff0c;创建minio文件夹 cd /optmkdir minio二&#xff0c;wget下载安装包 wget https://dl.minio.io/server/minio/release/linux-amd64/minio三&#xff0c;进入minio文件夹创建…

蓝海创意云vLive虚拟直播亮相2023昆山元宇宙产品展览会

2月15日-19日&#xff0c;由中国计算机行业协会“元宇宙创见未来”2023元宇宙产品展览会在江苏昆山隆重召开&#xff0c;共吸引了省内外32家企业参展&#xff0c;展出近百款元宇宙产品或技术&#xff0c;涵盖芯片、显示、VR、AR等硬件设备&#xff0c;以及工业、文旅、娱乐、教…

golang及goland的安装

1.电脑环境 2.软件下载 链接&#xff1a;https://pan.baidu.com/s/1YHM_jazftwkqRAuxJqMHZg 提取码&#xff1a;cdbm go1.17.5.windows-amd64.msi是go语言的开发及运行环境类似于Java的JDK。 goland-2020.2.2.exe 是go语言的开发工具(IDE),类似于Java的 Intelli J IDEA。 3…

【计算机网络】应用题方法总结

0.前言本篇博客主要记录自己在学习到的部分解决计算机网络应用题方法&#xff0c;主要参考视频如下&#xff1a;计算机网络期末复习 应用题_哔哩哔哩_bilibili【计算机网络】子网划分题型总结_哔哩哔哩_bilibili循环冗余码step 1&#xff1a;确定冗余码长度。多项式最高位即为冗…

阶段二11_面向对象高级_学生管理系统案例1

说明&#xff1a;学生管理系统案例需求和步骤1请查看上一张《阶段二10_面向对象高级_分类分包思想》 一.学生管理系统案例 步骤2&#xff1a;搭建主菜单和学生管理系统菜单 (0).主菜单和学习菜单界面和思路 界面&#xff1a; --------欢迎来到信息管理系统-------- 请输入您的…

CMake编译opencv4.6

openCV系列文章目录 文章目录openCV系列文章目录前言一、准备工作二、使用步骤1.使用CMake编译openCV总结前言 最近在项目中遇到图片处理&#xff0c;一拍脑袋就想到大名鼎鼎的opencv 一、准备工作 1.openCV官网下载 2.CMake官方下载 3.vs2019官方下载 二、使用步骤 1.使用…

[ vulhub漏洞复现篇 ] Drupal XSS漏洞 (CVE-2019-6341)

&#x1f36c; 博主介绍 &#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 _PowerShell &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【数据通信】 【通讯安全】 【web安全】【面试分析】 &#x1f389;点赞➕评论➕收藏 养成习…

Qt基础之二十九:图形视图框架(Graphics View Framework)及其应用

无意中从网络获取一份俄罗斯方块源码,基于图形视图框架(Graphics View Framework)实现的。当然源码的核心从来都不是界面,而是方块的移动、变形和消除等算法。源码非常完整,注释详细,经改动后已能在Qt5中运行,下面是运行效果,背景音乐和音效也是有的。 一.效果 二.原理 …

Spring MVC 源码 - HandlerMapping 组件(二)之 HandlerInterceptor 拦截器

HandlerMapping 组件HandlerMapping 组件&#xff0c;请求的处理器匹配器&#xff0c;负责为请求找到合适的 HandlerExecutionChain 处理器执行链&#xff0c;包含处理器&#xff08;handler&#xff09;和拦截器们&#xff08;interceptors&#xff09;handler 处理器是 Objec…

复旦大学邱锡鹏团队发布类 ChatGPT 模型MOSS

不知道这个人工智能&#xff0c;有没有获得完整的一生。 ChatGPT 是最先进的 AI&#xff0c;也是最热门的应用 —— 自去年 11 月底发布以来&#xff0c;它的月活跃用户两个月超过一亿&#xff0c;轻松拿到了全球互联网史上用户增长速度的第一。 它也是一种门槛很高的技术。由…

Metasploit 使用篇(二)

文章目录前言一、侦察1.banner信息2.http头检测小结二、枚举DNS枚举枚举文件三、漏洞扫描四、漏洞评估前言 内容均来自《Web渗透测试实战 基于Metasploit5.0》&#xff0c;包括渗透测试生命周期中的4个部分&#xff1a; 侦察Web应用枚举漏洞扫描漏洞评估 以对metasplitable …