多 线 程

1.什么是多线程?
有了多线程,我们就可以让程序同时做多件事情
2.多线程的作用?
提高效率
3.多线程的应用场景?
只要你想让多个事情同时运行就需要用到多线程
比如:软件中的耗时操作、所有的聊天软件、所有的服务器

1.进程和线程【理解】

  • 进程:是正在运行的程序

    独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位 动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的 并发性:任何进程都可以同其他进程一起并发执行

  • 线程:是进程中的单个顺序控制流,是一条执行路径

    单线程:一个进程如果只有一条执行路径,则称为单线程程序

    多线程:一个进程如果有多条执行路径,则称为多线程程序

2.并发和并行【理解】

  • 并发:在同一时刻,有多个指令在单个CPU上交替执行

  • 并行:在同一时刻,有多个指令在多个CPU上同时执行。

3.多线程的实现方式

  1. 继承Thread类的方式进行实现
  2. 实现Runnable接口的方式进行实现
  3. 利用Callable接口和Future接口方式实现

4.实现多线程方式一:继承Thread类【应用】

  1. 方法介绍

    方法名说明
    void run()在线程开启后,此方法将被调用执行
    void start()使此线程开始执行,Java虚拟机会调用run方法()
  2. 实现步骤

    1. 定义一个类MyThread继承Thread类

    2. 在MyThread类中重写run()方法

    3. 创建MyThread类的对象

    4. 启动线程

  3. 代码演示
public class MyThread extends Thread{@Overridepublic void run() {//书写线程要执行代码for (int i = 0; i < 100; i++) {System.out.println(getName() + "HelloWorld");}}
}
public class ThreadDemo {public static void main(String[] args) {/** 多线程的第一种启动方式:*   1. 自己定义一个类继承Thread*   2. 重写run方法*   3. 创建子类的对象,并启动线程* */MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}

两个小问题

  • 为什么要重写run()方法?

    因为run()是用来封装被线程执行的代码

  • run()方法和start()方法的区别?

    run():封装线程执行的代码,直接调用,相当于普通方法的调用

    start():启动线程;然后由JVM调用此线程的run()方法

5实现多线程方式二:实现Runnable接口【应用】

  • Thread构造方法

    方法名说明
    Thread(Runnable target)分配一个新的Thread对象
    Thread(Runnable target, String name)分配一个新的Thread对象
  • 实现步骤

    • 定义一个类MyRunnable实现Runnable接口

    • 在MyRunnable类中重写run()方法

    • 创建MyRunnable类的对象

    • 创建Thread类的对象,把MyRunnable对象作为构造方法的参数

    • 启动线程

代码演示

public class MyRun implements Runnable{@Overridepublic void run() {//书写线程要执行的代码for (int i = 0; i < 100; i++) {//获取到当前线程的对象/*Thread t = Thread.currentThread();System.out.println(t.getName() + "HelloWorld!");*/System.out.println(Thread.currentThread().getName() + "HelloWorld!");}}
}
public class ThreadDemo {public static void main(String[] args) {/** 多线程的第二种启动方式:*   1.自己定义一个类实现Runnable接口*   2.重写里面的run方法*   3.创建自己的类的对象*   4.创建一个Thread类的对象,并开启线程* *///创建MyRun的对象//表示多线程要执行的任务MyRun mr = new MyRun();//创建线程对象Thread t1 = new Thread(mr);Thread t2 = new Thread(mr);//给线程设置名字t1.setName("线程1");t2.setName("线程2");//开启线程t1.start();t2.start();}
}

6.实现多线程方式三: 实现Callable接口【应用】

  • 方法介绍

    方法名说明
    V call()计算结果,如果无法计算结果,则抛出一个异常
    FutureTask(Callable<V> callable)创建一个 FutureTask,一旦运行就执行给定的 Callable
    V get()如有必要,等待计算完成,然后获取其结果
  • 实现步骤

    1. 定义一个类MyCallable实现Callable接口

    2. 在MyCallable类中重写call()方法

    3. 创建MyCallable类的对象

    4. 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数

    5. 创建Thread类的对象,把FutureTask对象作为构造方法的参数

    6. 启动线程

    7. 再调用get方法,就可以获取线程结束之后的结果。

  • 代码演示

import java.util.concurrent.Callable;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {//求1~100之间的和int sum = 0;for (int i = 1; i <= 100; i++) {sum = sum + i;}return sum;}
}import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class ThreadDemo {public static void main(String[] args) throws ExecutionException, InterruptedException {/**   多线程的第三种实现方式:*       特点:可以获取到多线程运行的结果**       1. 创建一个类MyCallable实现Callable接口*       2. 重写call (是有返回值的,表示多线程运行的结果)**       3. 创建MyCallable的对象(表示多线程要执行的任务)*       4. 创建FutureTask的对象(作用管理多线程运行的结果)*       5. 创建Thread类的对象,并启动(表示线程)* *///创建MyCallable的对象(表示多线程要执行的任务)MyCallable mc = new MyCallable();//创建FutureTask的对象(作用管理多线程运行的结果)FutureTask<Integer> ft = new FutureTask<>(mc);//创建线程的对象Thread t1 = new Thread(ft);//启动线程t1.start();//获取多线程运行的结果Integer result = ft.get();System.out.println(result);}
}

三种实现方式对比:

优点缺点
继承Thread类

编程比较简单,可以直接使用

Thread类中的方法

可以扩展性较差,

不能再继承其他的类

实现Runnable接口扩展性强,实现该接口的同时还可以继承其他的类编程相对复杂,不能直接使用Thread类中的方法
实现callable接口

7.设置和获取线程名称线程休眠【应用】

  • 方法介绍

    方法名说明
    void setName(String name)将此线程的名称更改为等于参数name
    String getName()返回此线程的名称
    Thread currentThread()返回对当前正在执行的线程对象的引用
    static void sleep(long millis)使当前正在执行的线程停留(暂停执行)指定的毫秒数

  void setName(String name)           设置线程的名字(构造方法也可以设置名字)
            细节:
                1、如果我们没有给线程设置名字,线程也是有默认的名字的
                        格式:Thread-X(X序号,从0开始的)
                2、如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置

static Thread currentThread()       获取当前线程的对象
            细节:
                当JVM虚拟机启动之后,会自动的启动多条线程
                其中有一条线程就叫做main线程
                他的作用就是去调用main方法,并执行里面的代码
                在以前,我们写的所有的代码,其实都是运行在main线程当中

  static void sleep(long time)        让线程休眠指定的时间,单位为毫秒
            细节:
                1、哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间
                2、方法的参数:就表示睡眠的时间,单位毫秒
                    1 秒= 1000毫秒
                3、当时间到了之后,线程会自动的醒来,继续执行下面的其他代码
      

package com.itheima.a04threadmethod1;public class MyThread extends Thread{public MyThread() {}public MyThread(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 100; i++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(getName() + "@" + i);}}
}package com.itheima.a04threadmethod1;public class ThreadDemo {public static void main(String[] args) throws InterruptedException {/*String getName()                    返回此线程的名称void setName(String name)           设置线程的名字(构造方法也可以设置名字)细节:1、如果我们没有给线程设置名字,线程也是有默认的名字的格式:Thread-X(X序号,从0开始的)2、如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置static Thread currentThread()       获取当前线程的对象细节:当JVM虚拟机启动之后,会自动的启动多条线程其中有一条线程就叫做main线程他的作用就是去调用main方法,并执行里面的代码在以前,我们写的所有的代码,其实都是运行在main线程当中static void sleep(long time)        让线程休眠指定的时间,单位为毫秒细节:1、哪条线程执行到这个方法,那么哪条线程就会在这里停留对应的时间2、方法的参数:就表示睡眠的时间,单位毫秒1 秒= 1000毫秒3、当时间到了之后,线程会自动的醒来,继续执行下面的其他代码*///1.创建线程的对象MyThread t1 = new MyThread("飞机");MyThread t2 = new MyThread("坦克");//2.开启线程t1.start();t2.start();//哪条线程执行到这个方法,此时获取的就是哪条线程的对象/* Thread t = Thread.currentThread();String name = t.getName();System.out.println(name);//main*//*System.out.println("11111111111");Thread.sleep(5000);System.out.println("22222222222");*/}
}

8线程优先级【应用】

  • 线程调度

    • 两种调度方式

      • 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片

      • 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些

    • Java使用的是抢占式调度模型

    • 随机性

      假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的

优先级相关方法

方法名说明
final int getPriority()返回此线程的优先级
final void setPriority(int newPriority)更改此线程的优先级线程默认优先级是5;线程优先级的范围是:1-10
public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + "---" + i);}}
}public class ThreadDemo {public static void main(String[] args){/*setPriority(int newPriority)        设置线程的优先级final int getPriority()             获取线程的优先级*///创建线程要执行的参数对象MyRunnable mr = new MyRunnable();//创建线程对象Thread t1 = new Thread(mr,"飞机");Thread t2 = new Thread(mr,"坦克");t1.setPriority(1);t2.setPriority(10);t1.start();t2.start();}
}

9.守护线程【应用】

当普通线程执行完之后,那么守护线程也没有继续运行下去的必要了.

  • 相关方法

    方法名说明
    void setDaemon(boolean on)将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
public class MyThread1 extends Thread {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(getName() + "---" + i);}}
}
public class MyThread2 extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(getName() + "---" + i);}}
}
public class Demo {public static void main(String[] args) {MyThread1 t1 = new MyThread1();MyThread2 t2 = new MyThread2();t1.setName("女神");t2.setName("备胎");//把第二个线程设置为守护线程//当普通线程执行完之后,那么守护线程也没有继续运行下去的必要了.t2.setDaemon(true);t1.start();t2.start();}
}

10.礼让线程

     相关方法

方法名说明
public static void yield()出让线程/礼让线程
public class MyThread extends Thread{@Overridepublic void run() {//"飞机"  "坦克"for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);//表示出让当前CPU的执行权Thread.yield();}}
}
public class ThreadDemo {public static void main(String[] args) {/*public static void yield()      出让线程/礼让线程*/MyThread t1 = new MyThread();MyThread t2 = new MyThread();t1.setName("飞机");t2.setName("坦克");t1.start();t2.start();}
}

11.插入线程

相关方法

方法名说明
public final void join()

 插入线程/插队线程

public class MyThread extends Thread{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(getName() + "@" + i);}}
}
public class ThreadDemo {public static void main(String[] args) throws InterruptedException {/*public final void join()  插入线程/插队线程*/MyThread t = new MyThread();t.setName("土豆");t.start();//表示把t这个线程,插入到当前线程之前。//t:土豆//当前线程: main线程t.join();//执行在main线程当中的for (int i = 0; i < 10; i++) {System.out.println("main线程" + i);}}
}

12.线程的生命周期

13.线程的安全问题

  • 安全问题出现的条件

    • 是多线程环境

    • 有共享数据

    • 有多条语句操作共享数据

  • 如何解决多线程安全问题呢?

    • 基本思想:让程序没有安全问题的环境

怎么实现呢?

  • 把多条语句操作共享数据的代码给起来,让任意时刻只能有一个线程执行即可

  • Java提供了同步代码块的方式来解决

同步代码块格式:

synchronized(任意对象) { 
                    多条语句操作共享数据的代码 
}

synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁

特点1:锁默认打卉,有一个线程进去了,锁自动关闭
特点2:里面的代码全部执行完毕,线程出来,锁自动打开

细节:

1.锁必须放在循环里面

2.锁必须唯一,一般都写成当前类的字节码文件 如:synchronized (MyThread.class) {}

同步的好处和弊端

  • 好处:解决了多线程的数据安全问题

  • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

14.同步方法解决数据安全问题【应用】

  • 同步方法的格式

    同步方法:就是把synchronized关键字加到方法上

    修饰符 synchronized 返回值类型 方法名(方法参数) { 方法体;
    }

    特点1.同步方法是锁住方法里面所有的代码

  • 特点2.锁对象不能自己指定

    同步方法的锁对象是什么呢?

    非静态:this

  • 静态同步方法

    同步静态方法:就是把synchronized关键字加到静态方法上

    修饰符 static synchronized 返回值类型 方法名(方法参数) { 方法体;
    }

    同步静态方法的锁对象是什么呢?

    类名.class

15Lock锁【应用】

虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

  • ReentrantLock构造方法

    方法名说明
    ReentrantLock()创建一个ReentrantLock的实例
  • 加锁解锁方法

    方法名说明
    void lock()获得锁
    void unlock()释放锁
package com.itheima.a11threadsafe3;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class MyThread extends Thread{static int ticket = 0;static Lock lock = new ReentrantLock();@Overridepublic void run() {//1.循环while(true){//2.同步代码块//synchronized (MyThread.class){lock.lock(); //2 //3try {//3.判断if(ticket == 100){break;//4.判断}else{Thread.sleep(10);ticket++;System.out.println(getName() + "在卖第" + ticket + "张票!!!");}//  }} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}
}

16死锁【理解】

  • 概述

    线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行

  • 什么情况下会产生死锁

    1. 资源有限

    2. 同步嵌套

17生产者和消费者案例【应用】

Object类的等待和唤醒方法

方法名说明
void wait()导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
void notify()唤醒正在等待对象监视器的单个线程
void notifyAll()唤醒正在等待对象监视器的所有线程
  • 案例需求

    • 桌子类(Desk):定义表示包子数量的变量,定义锁对象变量,定义标记桌子上有无包子的变量

    • 生产者类(Cooker):实现Runnable接口,重写run()方法,设置线程任务

      1.判断是否有包子,决定当前线程是否执行

      2.如果有包子,就进入等待状态,如果没有包子,继续执行,生产包子

      3.生产包子之后,更新桌子上包子状态,唤醒消费者消费包子

    • 消费者类(Foodie):实现Runnable接口,重写run()方法,设置线程任务

      1.判断是否有包子,决定当前线程是否执行

      2.如果没有包子,就进入等待状态,如果有包子,就消费包子

      3.消费包子后,更新桌子上包子状态,唤醒生产者生产包子

    • 测试类(Demo):里面有main方法,main方法中的代码步骤如下

      创建生产者线程和消费者线程对象

      分别开启两个线程

18线程的六种状态

19线程池

线程池的设计思路 :

  1. 准备一个任务容器

  2. 一次性启动多个(2个)消费者线程

  3. 刚开始任务容器是空的,所以线程都在wait

  4. 直到一个外部线程向这个任务容器中扔了一个"任务",就会有一个消费者线程被唤醒

  5. 这个消费者线程取出"任务",并且执行这个任务,执行完毕后,继续等待下一次任务的到来

核心原理:

  1. 创建一个池子,池子中是空的
  2. 提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子,下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
  3. 但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待
     

线程池代码实现
1,创建线程池
2,提交任务
3,所有的任务全部执行完毕,关闭线程池


Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。

public static ExecutorService newCachedThreadPool()            创建一个没有上限的线程池 public static newFixedThreadPool(int nThreads)                                创建一个有上限的线程池

public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 1; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + "---" + i);}}
}import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class MyThreadPoolDemo {public static void main(String[] args) throws InterruptedException {/*public static ExecutorService newCachedThreadPool()             创建一个没有上限的线程池public static ExecutorService newFixedThreadPool (int nThreads) 创建有上限的线程池*///1.获取线程池对象ExecutorService pool1 = Executors.newFixedThreadPool(3);//2.提交任务pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());pool1.submit(new MyRunnable());//3.销毁线程池//pool1.shutdown();}
}

自定义线程池

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor
        (核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);

        参数一:核心线程数量              不能小于0
        参数二:最大线程数                不能小于0,最大数量 >= 核心线程数量
        参数三:空闲线程最大存活时间       不能小于0
        参数四:时间单位                  用TimeUnit指定
        参数五:任务队列                  不能为null
        参数六:创建线程工厂              不能为null
        参数七:任务的拒绝策略             不能为null

任务拒绝策略

任务拒绝策略说明
ThreadPoolExecutor. AbortPolicy默认策略:丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy丢弃任务,但是不抛出异常这是不推荐的做法
ThreadPoolExecutor.Discardoldestpolicy抛弃队列中等待最久的任务然后把当前任务加入队列中
ThreadPoolExecutor.callerRunsPolicy调用任务的run()方法绕过线程池直接执行
 ThreadPoolExecutor pool = new ThreadPoolExecutor(3,  //核心线程数量,能小于06,  //最大线程数,不能小于0,最大数量 >= 核心线程数量60,//空闲线程最大存活时间TimeUnit.SECONDS,//时间单位new ArrayBlockingQueue<>(3),//任务队列Executors.defaultThreadFactory(),//创建线程工厂new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略

最大并行数:自己电脑的处理器个数

线程池多大合适?

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

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

相关文章

Day36|贪心算法part05:435. 无重叠区间、763.划分字母区间、56. 合并区间

435. 无重叠区间 有了上题射气球的因子&#xff0c;这题也就有思路了&#xff0c;反正无脑排序就行了&#xff1a; 首先将所有区间按照end的大小从小到大排序&#xff1b;选取最早end为起始x_end遍历所有区间&#xff0c;如果该区间的start比end大&#xff08;可重叠&#xf…

活动预告|如何构建云原生现代化数据栈?北京首场 Meetup 来啦!

数字化时代带来了海量的数据涌现&#xff0c;传统的数据架构已然无法满足现代企业的需求&#xff0c;现代化数据栈应运而生。基于云原生的现代化数据栈具备了多云兼容的特性&#xff0c;在不同的云环境下能够保持高性能运作&#xff0c;使企业得以无缝地处理和分析海量的数据集…

利用SARscape对日本填海造陆和天然气开采进行地表形变监测

日本千叶市&#xff0c;是日本南部重要的工业港市。位于西部的浦安市是一个典型的"填海造田"城市&#xff0c;东南部的东金区有一片天然气开采区域&#xff0c;本文利用SARscape&#xff0c;用干涉叠加的方法&#xff0c;即PS和SBAS&#xff0c;对这两个区域进行地表…

倒计时4天!百度Create AI开发者大会“大模型与深度学习技术”论坛亮点抢鲜看!

作为人工智能的核心基础技术&#xff0c;深度学习具有很强的通用性&#xff0c;大模型技术在深度学习的基础上&#xff0c;通过构建更加庞大神经网络模型和应用transformer等更加领先的算法&#xff0c;使模型的处理能力产生质的飞跃。飞桨&#xff08;PaddlePaddle&#xff09…

MySQL分区表(14/16)

分区表 基本概述 分区表是数据库中一种用于优化大型表数据管理和查询性能的技术。它将一个表的数据根据特定的规则或条件分割成多个部分&#xff0c;每个部分称为一个分区。每个分区可以独立于其他分区进行存储、管理和查询&#xff0c;这样可以提高数据处理的效率&#xff0…

VS Code中“@“符号如何自动补全导入路径

一、下载 Path Intellisense 插件 二、打开设置&#xff0c;在扩展中选择该插件&#xff0c;点击setting.json 三、添加配置&#xff1a; "":"${workspaceRoot}/src" 如图&#xff1a; 四、在项目src目录中新建jsconfig.json文件 &#xff08;一定要是src目…

动态规划(背包问题)

一:动态规划概述: 动态规划实际上是一种将原本的 大 方面的问题转化为许许多多的 小方面 的一种应用, 在一定程度上避免数据的重复, 并且能够将数据以自己希望的方式进行存储, 用来解决多阶段的数学问题, 从而提高算法的效率 在算法当中, 动态规划主要包括有: 递推, 线性DP 记忆…

【Java核心技术】第3章 Java的基本程序设计结构

1 数据类型 Java一共有8种数据类型&#xff1a; 4种整型 类型存储需求int4字节short2字节long8字节byte1字节 2种浮点型 类型存储需求float4字节double8字节 1种字符型 1种布尔型 2 变量声明 2.1 局部类型推断 如果可以从变量的初始值推断变量类型&#xff0c;只需要使用…

【数组】5螺旋矩阵

这里写自定义目录标题 一、题目二、解题精髓-循环不变量三、代码 一、题目 给定⼀个正整数 n&#xff0c;⽣成⼀个包含 1 到 n^2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的正⽅形矩阵。 示例: 输⼊: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ] 二、解题精髓…

JVM参数列表

-client :设置JVM使用client模式,特点启动较快(神机不明显(I5/8G/SSD)) -server :设置JVM使用server模式。64位JDK默认启动该模式 -agentlib:libname[options] :用于加载本地的lib -agentlib:hprof :用于获取JVM的运行情况 -agentpath:pathnamep[options] :加载制定路径的本…

Day:007(1) | Python爬虫:高效数据抓取的编程技术(scrapy框架使用)

Scrapy的介绍 Scrapy 是一个用于抓取网站和提取结构化数据的应用程序框架&#xff0c;可用于各种有用的应用程序&#xff0c;如数据挖掘、信息处理或历史存档。 尽管 Scrapy 最初是为网络抓取而设计的&#xff0c;但它也可用于使用API提取数据或用作通用网络爬虫。 Scrapy的优势…

【Nacos】Nacos最新版的安装、配置过程记录和踩坑分享

Nacos是什么&#xff1f;有什么功能&#xff1f;大家可以自行联网&#xff08;推荐 https://cn.bing.com/&#xff09;搜索&#xff0c;这里就不做介绍了。 简单的看了下官网&#xff0c;安装最新版的Nacos&#xff08;v2.3.2&#xff09;需要使用到JDK&#xff08;1.8.0&…

应急响应-战前反制主机HIDSElkeid蜜罐系统HFish

知识点 战前-反制-平台部署其他更多项目&#xff1a; https://github.com/birdhan/SecurityProduct HIDS&#xff1a;主机入侵检测系统&#xff0c;通常会有一个服务器承担服务端角色&#xff0c;其他主机就是客户端角色&#xff0c;客户端加入到服务端的检测范围里&#xff…

滑动窗口用法

文章目录 1. 长度最小的子数组&#xff08;模板&#xff09;2. 无重复字符的最长字串3. 最小覆盖字串4. 加油站5. 替换字串得到平衡字符串 1. 长度最小的子数组&#xff08;模板&#xff09; 题目分析 直接用步骤分析示例1&#xff0c;[]表示窗口&#xff0c;min_length表示满…

软件测试级别和对应要求

软件测试级别指的是将软件测试活动按照不同的开发阶段和测试目的进行分类&#xff0c;形成不同层次的测试过程。 分级依据&#xff0c;根据V模型 单元测试&#xff1a;这是软件生命周期中的第一个测试级别&#xff0c;主要针对软件的最小单元模块进行&#xff0c;例如类、函数…

Robotstudio2024中从备份文件恢复和创建工作站的具体方法演示

Robotstudio2024中从备份文件恢复和创建工作站的具体方法演示 如下图所示,打开Robotstudio2024软件,有需要的可以从以下链接获取: ABB机器人编程仿真软件RobotStudio 2024.1-链接baiduyun 点击“新建”—工作站—创建, 如下图所示,点击“ABB模型库”,选择自己使用的机器…

Proxmox VE qm 方式一键创建Linux虚拟机

前言 实现qm 方式一键创建Linux虚拟机&#xff0c;提高效率。 qm 一键创建Linux 虚拟机 以下实现在线下载镜像&#xff0c;创建虚拟机&#xff0c;安装系统需要自己手动安装哦&#xff0c;如果想实现全自动安装系统&#xff0c;建议部署自己的内网pxe server 系统参考各参数…

EditPlus来啦(免费使用!)

hello&#xff0c;我是小索奇 今天推荐一款编辑器&#xff0c;是索奇学习JavaSE时入手滴&#xff0c;非常好用哈&#xff0c;小索奇还是通过老杜-杜老师入手滴&#xff0c;相信很多人也是通过老杜认识嘞&#xff0c;来寻找破解版或者准备入手这个间接使用的编辑器~ EditPlus是…

HarmonyOS开发实例:【菜单app】

简介 分布式菜单demo 模拟的是多人聚餐点菜的场景&#xff0c;不需要扫码关注公众号等一系列操作&#xff0c;通过分布式数据库可以方便每个人可及时查看到订单详情&#xff0c;数量&#xff0c;总额等&#xff1b;效果如下 demo效果 工程目录 完整的项目结构目录如下 ├…

如何编译OpenHarmony自带APP

作者&#xff1a;王石 概述 OpenHarmony 的主干代码是开源社区的重要学习资源&#xff0c;对于想进行应用开发和熟悉 OpenHarmony 能力的同学主干代码是非常重要的资源&#xff0c;在主干代码的 applications 目录里聚集了很多原生的应用实现&#xff0c;那么如何编译这些代码…