Java——线程池详细讲解

news2024/11/15 22:59:22

文章目录

  • 一、线程池
  • 一、线程池基础
    • 1.1 什么是线程池
    • 1.2 为什么使用线程池
    • 1.3 线程池有哪些优势
    • 1.4 应用场景
  • 二、线程池使用
    • 2.1 Java内置线程池 ThreadPoolExecutor
      • 2.1.1 线程池的七个参数
        • 2.1.1.1 **int corePoolSize 核心线程数量**
        • 2.1.1.2 int maximumPoolSize 最大线程数
        • 2.1.1.3 long keepAliveTime 最大空闲时间
        • 2.1.1.4 TimeUnit unit 时间单位
        • 2.1.1.5 BlockingQueue<Runnable> workQueue 任务队列
        • 2.1.1.6 ThreadFactory threadFactory 线程工厂
        • 2.1.1.7 RejectedExecutionHandler handler 饱和处理机制
    • 2.2 线程池工作流程介绍
  • 三、自定义线线程池
    • 3.1 参数设计分析
      • 3.1.1 核心线程数量 corePoolSize
      • 3.1.2 任务队列长度 workQueue
      • 3.1.3 最大线程数 maximumPoolSize
      • 3.1.4 最大空闲时间 KeepAliveTime
    • 3.2 自定义线程池实现步骤
      • 3.2.1 编写任务类(MyTask) 实现Runnable接口
      • 3.2.2 编写线程类(MyWork) 用于执行任务 需要持有所有任务
      • 3.2.3 编写线程池类(MyThreadPool) 包含提交任务,执行任务能力
      • 3.2.4 编写测试类 创建线程池对象,提交多个任务测试
  • 四、 Java内置线程池 - ExecutorService 介绍
    • 4.1 常用方法
    • 4.2 ExecutorService 获取
      • 4.2.1 static ExecutorService newCachedThreadPool ( )
      • 4.2.2 static ExecutorService newCachedThreadPool (ThreadFactory threadFactory)
      • 4.2.3 static ExecutorService newFixedThreadPool(int nThreads)
      • 4.2.4 static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
      • 4.2.5 static ExecutorService newSingleThreadExecutor()
      • 4.2.6 static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)
  • 五、Java内置线程池 - ScheduledExecutorService
    • 5.1 ScheduledExecutorService 获取
      • 5.1.1 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
      • 5.1.2 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)
      • 5.1.3 static ScheduledExecutorService newSingleThreadScheduledExecutor()
      • 5.1.4 static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
    • 5.2 ScheduledExecutorService 常用方法
      • 5.2.1 <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
      • 5.2.2 ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
      • 5.2.3 ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
      • 5.2.4 ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
  • 六、 Future - 异步计算结果
    • 6.1 常用方法
    • 6.2 Future演示
  • 七、 综合案例
    • 7.1 案例
    • 7.2 代码

一、线程池

​ 线程池是一种常见的多线程编程技术,它可以在执行任务时复用已创建的多个线程,并且可以控制同时运行的线程数以避免资源占用过多的问题。下面是一个简单的Java示例代码,演示如何使用线程池。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5); // 创建一个容量为 5 的线程池
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Task(i)); // 提交任务,由线程池中空闲的线程执行
        }
        executorService.shutdown(); // 调用任务完成,关闭线程池
    }

    static class Task implements Runnable {
        private int taskNum;

        public Task(int num) {
            this.taskNum = num;
        }

        @Override
        public void run() { // 线程池中的线程会调用该方法进行具体任务的执行
            System.out.println("正在执行task " + taskNum);
            try {
                Thread.sleep(1000); // 模拟任务耗时
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("task " + taskNum + " 执行完毕");
        }
    }
}

​ 上述代码定义了一个包含10个任务的线程池(容量为5),每个任务被分配到池中可用的空闲线程,当线程完成任务后,它会返回线程池以便其他任务使用。当所有任务完成时,调用executorService.shutdown()方法关闭线程池。

​ 总之,在编写多线程应用程序时,利用线程池可以更有效地管理资源和提高系统性能。同时,由于在线程池中的线程具有重复使用性、复用性和恰当的个数等优点,因此建议在需要频繁创建并执行线程的情况下,尽可能使用线程池来实现。

一、线程池基础

1.1 什么是线程池

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。

​ 这里的线程就是我们前面学过的线程,这里的任务就是我们前面学过的实现了Runnable或Callable接口的实例对象

1.2 为什么使用线程池

​ 使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统运行运行压力;当然了,使用线程池的原因不仅仅只有这些,我们可以从线程池自身的优点上来进一步了解线程池的好处;

1.3 线程池有哪些优势

  • 线程和任务分离,提升线程重用性
  • 控制线程并发数量,降低服务器压力,统一管理所有线程
  • 提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么线程池就免去了T1和T3的时间

1.4 应用场景

应用场景介绍:

  • 网购商品秒杀
  • 云盘文件上传和下载
  • 12306网上购票系统

只要有并发的地方、任何数量大或小、每个任务执行时间长活短都可以使用线程池

只不过在使用线程池的时候,注意一下设置合理的线程池大小即可

二、线程池使用

2.1 Java内置线程池 ThreadPoolExecutor

2.1.1 线程池的七个参数

2.1.1.1 int corePoolSize 核心线程数量

线程池中的核心线程数量指的是线程池在正常情况下需要保持的最小线程数量,当我们执行任务时线程线程数量没有达到核心线程数量,那就会新开线程。

​ 当有任务提交到线程池时,线程池会先尝试通过核心线程来处理任务。只有当核心线程都被占用,并且任务队列已满时,线程池才会创建新的线程。因此,核心线程数量的大小可以直接影响线程池的性能和资源消耗。如果核心线程数量设置过大,可能会浪费系统资源;如果设置过小,可能会导致任务等待时间增加,进而降低系统的响应速度。在实际应用中,应根据具体的业务场景和系统负载情况来合理设置线程池的核心线程数量。

2.1.1.2 int maximumPoolSize 最大线程数

线程池中的最大线程数量指的是线程池允许创建的最大线程数,包括核心线程和非核心线程。

当任务队列已满,并且当前线程数小于最大线程数时,线程池会创建新的线程来处理任务。但是设置过大的最大线程数可能会带来一些负面影响,如增加系统资源消耗和降低系统的稳定性。因此,在合理使用线程池进行任务调度的前提下,需要根据业务场景和系统负载情况合理设置线程池的最大线程数量。

2.1.1.3 long keepAliveTime 最大空闲时间

​ 也可以叫做存活时间。

​ 当我们的一个线程不用的时候允许空闲,但是空闲到一定时间之后线程池也会回收线程。

​ 线程池中的最大空闲时间指的是一个非核心线程在空闲状态下保持存活的最长时间。

​ 当线程池中的非核心线程数量超过了核心线程数量时,超过核心线程数量的这些线程被称为“非核心线程”。

​ 这些非核心线程在处理完任务后并不会立即销毁,而是处于等待下一次任务的空闲状态。线程池中的最大空闲时间设置了一个阈值,若一个非核心线程在空闲状态下超过了该时间限制,线程池就会将其销毁,以释放系统资源。

合理设置线程池的最大空闲时间可以有效避免因长时间运行的线程造成的资源浪费和系统负载过高的问题。但是如果将最大空闲时间设置得太短,则可能导致过多的线程被销毁和创建,反而会影响性能。正确地设置线程池的参数需要综合考虑业务场景和系统负载情况,并通过实验和观测对其进行调优

2.1.1.4 TimeUnit unit 时间单位

枚举类

image-20230426113405452

2.1.1.5 BlockingQueue workQueue 任务队列

作用:暂存尚未执行的任务的数据结构(临时缓冲区)

当我们的线程数量达到核心线程数量后,如果再有任务提交到线程池里面,此时不会里面创建新线程,而是将任务添加到任务队列里面去,只有当任务队列加满后,按照我们设置的最大线程数来逐步创建线程(创建的线程一定不大于最大线程数)

2.1.1.6 ThreadFactory threadFactory 线程工厂

允许我们自己参与创建线程的过程

 class SimpleThreadFactory implements ThreadFactory { 
     public Thread newThread(Runnable r) { 
         return new Thread(r);
     } 
 } 

2.1.1.7 RejectedExecutionHandler handler 饱和处理机制

​ 线程数到达核心线程数量,并且任务队列满了,线程数也到达最大线程数了, 也就是说线程池处于饱和状态了,已经无法再融入其他任务了,那这个时候我们就能给出一个饱和处理机制。

​ 比如让那些任务等一会啊,抛弃一些任务啊

  • 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂和拒绝执行处理程序。

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)

  • 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认线程工厂。

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)

  • 创建一个新的 ThreadPoolExecutor与给定的初始参数和默认拒绝执行处理程序。

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)

  • 创建一个新 ThreadPoolExecutor给定的初始参数。

    ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)

2.2 线程池工作流程介绍

image-20230426114734896

image-20230426115721098

三、自定义线线程池

3.1 参数设计分析

3.1.1 核心线程数量 corePoolSize

​ 核心线程数的设计需要依据任务的处理时间和每秒产生的任务数量来确定

​ 例如:执行一个任务需要0.1秒,系统百分之80的时间每秒都会产生100个任务,那么要想在1秒内处理完这100个务,就需要10个线程,此时我们就可以设计核心线程数为10;当然实际情况不可能这么平均,所以我们一般按照8020原则设计即可,既按照百分之80的情况设计核心线程数,剩下的百分之20可以利用最大线程数处理

3.1.2 任务队列长度 workQueue

任务队列长度一般设计为:核心线程数/单个任务执行时间成×2即可

例如上面的场景中,核心线程数设计为10,单个任务执行时间为0.1秒,则队列长度可以设计为200

3.1.3 最大线程数 maximumPoolSize

最大线程数的设计除了需要参照核心线程数的条件外,还需要参照系统每秒产生的最大任务数决定

例如:上述环境中,如果系统每秒最大产生的任务是1000个,那么,

最大线程数=(最大任务数-任务队列长度)×单个任务执行时间 : 最大线程数=(1000-200)×0.1=80

3.1.4 最大空闲时间 KeepAliveTime

这个参数的设计完全参考系统运行环境和硬件压力设定,没有固定的参考值。

用户可以根据经验和系统产生任务的时间间隔合理设置一个值即可。

3.2 自定义线程池实现步骤

3.2.1 编写任务类(MyTask) 实现Runnable接口

/**
 * 要求:
 *    自定义线程池练习,这是任务类,需要实现接口
 *    包含任务编号,每一个任务执行时间设计为0.2秒
 */
public class MyTask implements Runnable {

    private int id ; //  任务编号

//   由于run方法是重写接口中的方法,不可添加形式参数,但是我们可以通过构造方法添加

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println("线程:"+name+"即将执行任务:"+id);
        try {
            Thread.sleep(200);  //休眠200毫秒
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程:"+name+"完成了任务:"+id);
    }
    
    public MyTask(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}

3.2.2 编写线程类(MyWork) 用于执行任务 需要持有所有任务

所有的任务用一个集合存起来


/**
 *  需求:
 *     编写一个线程类,需要去继承Thread,设计一个属性,用于保存线程的名字
 *     设计一个集合,用于保存所有的任务
 */
public class MyWorker extends Thread{

    private String name;  //保存线程名字
    private List<Runnable> tasks;  //保存将来所有的任务

    /**
     *  判断集合中是否有任务,只要有任务,就一直执行
     */
    @Override
    public void run() {
        while (tasks.size()>0){
//          将第一个任务移除出来
            Runnable runnable = tasks.remove(0);
//          一直执行任务
            runnable.run();
        }
    }

//  利用构造方法给成员变量赋值
    public MyWorker(String name, List<Runnable> tasks) {
        super(name);
//        this.name = name;
        this.tasks = tasks;
    }
}

3.2.3 编写线程池类(MyThreadPool) 包含提交任务,执行任务能力

/**
 *  自定义的线程池类
 *  成员变量:
 *    1. 任务队列    集合来表示即可(线程安全的集合)
 *    2. 当前线程数量
 *    3. 核心线程数量
 *    4. 最大线程数
 *    5. 任务队列长度
 *   没有设置最大空闲时间
 *
 *  成员方法:
 *    1.提交任务:将任务添加到集合中(如果没有超出任务队列的长度则可以加入到任务队列中)
 *    2.执行任务: 判断当前线程的数量,决定创建核心线程(如果当前线程数量在核心线程数量之下)还是非核心线程(当前线程数量在核心线程数量与最大线程数量之间)
 */
public class MyThreadPool {
//    1. 任务队列  集合来表示即可(线程安全的集合)
//         LinkedList集合是非线程安全的,我们可以使用集合工具类中方法将其转变为线程安全的
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());

//     2. 当前线程数量,初始时是0
    private int num;

//     3. 核心线程数量
    private int corePoolSize;

//     4. 最大线程数
    private int maxSize;

//    5. 任务队列长度
    private int workSize;

//    1.提交任务:
    public void submit(Runnable runnable){
//       判断当前集合中任务的数量,是否超出了最大任务数量
        if (tasks.size()>=workSize){
//          TODO  简陋的饱和机制处理
            System.out.println("任务:"+runnable+"被丢弃了");
        }else {
            tasks.add(runnable);
//          TODO 执行任务
            execTask(runnable);  //这个地方最好传入线程,不传入任务
        }
    }


//   2.执行任务:
    private void execTask(Runnable runnable) {
//      TODO 判断当前线程池中的线程总数量,是否超出了核心数
        if(num <=corePoolSize){
            MyWorker myWorker = new MyWorker("核心线程:"+num,tasks);
//            Thread t = new Thread(myWorker);
//          启动线程
            myWorker.start();
            num++;
        }else if (num < maxSize){
            MyWorker myWorker = new MyWorker("非核心线程:"+num,tasks);
//            Thread t = new Thread(myWorker);
//          启动线程
            myWorker.start();
            num++;
        }else {
//            因为我们在execTask之前,已经将任务添加到tasks集合中了,所以说是被缓存了
            System.out.println("任务:"+runnable+"被缓存了");
        }
    }

    public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
        this.corePoolSize = corePoolSize;
        this.maxSize = maxSize;
        this.workSize = workSize;
    }
}

3.2.4 编写测试类 创建线程池对象,提交多个任务测试

/**
 * 1.创建线程池对象
 * 2.提交多个任务
 */
public class MyTest {
    public static void main(String[] args) {
//        1.创建线程池对象
        MyThreadPool pool = new MyThreadPool(2,4,20);

//        2.提交多个任务
        for(int i=0;i<10;i++){
//        3. 创建任务对象,并提交给线程池
           MyTask myTask = new MyTask(i);
           pool.submit(myTask);
        }

    }
}

image-20230426155202107

四、 Java内置线程池 - ExecutorService 介绍

ExecutorService 接口是Java内置的线程池接口。

接口是无法直接创建对象的。

4.1 常用方法

  • void shutdown()

启动一次顺序关闭,执行以前提交的任务,但不接受新任务,即无法向线程池中提交任务,但是之前提交的任务会继续执行

  • List shutdownNow ( )

    停止所有正在执行的任务,暂停处理正在等待的任务,并返回等待执行的任务列表(使用返回值的)

  • Future submit(Callable task )

    方法重载

    执行带返回值的任务,返回一个Future对象.

  • Future<?> submit(Runnable task)

    方法重载

    执行 Runnable 任务,并返回一个表示该任务的 Future。

  • Future submit ( Runnable task,T result)

    方法重载

    执行 Runnable 任务,并返回一个表示该任务的 Future。

4.2 ExecutorService 获取

ExecutorService 是一个接口,无法直接创建,但是可以利用JDK中的Executors类(工厂类)中的静态方法来获取ExecutorService 对象

4.2.1 static ExecutorService newCachedThreadPool ( )

创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建

此方法获取的线程池的最大空闲时间是60秒

这个方法对线程的数量不做限制的,有多少任务,就创建多少线程,即优先执行任务,效率优先

/**
 * 练习Executors获取ExecutorService,然后调用方法提交任务
 */
public class MyTest01 {
    public static void main(String[] args) {
//      1.使用工厂类获取线程池对象
        ExecutorService executorService = Executors.newCachedThreadPool();
//      2. 提交任务:
        for (int i=0; i<10;i++){
            MyRunnable myRunnable = new MyRunnable(i);
            executorService.submit(myRunnable);
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中打印出是哪一个线程正在执行任务
 */
class MyRunnable implements Runnable{

    private int id;

    @Override
    public void run() {
//     获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务...."+id);
    }

    public MyRunnable(int id) {
        this.id = id;
    }
}

image-20230426170846365

4.2.2 static ExecutorService newCachedThreadPool (ThreadFactory threadFactory)

**指定线程的创建方式 **

线程池中的所有线程都使用ThreadFactory来创建,这样的线程无需手动启动,自动执行

ThreadFactory 也是一个接口,只不过允许程序员自己写实现类,在实现类内部创建线程对象 ,相当于程序员可以控制线程池中每一个线程对象的创建

/**
 * 练习Executors获取ExecutorService,然后调用方法提交任务
 */
public class MyTest01 {
    public static void main(String[] args) {
//      1.使用工厂类获取线程池对象
        ExecutorService executorService = Executors.newCachedThreadPool(new ThreadFactory() {
            int n=1;
            @Override
            public Thread newThread(Runnable r) {
//              创建的线程和任务r绑定在一起,就可以执行了
                return new Thread(r,"自定义的线程名称"+n++);
            }
        });
//      2. 提交任务:
        for (int i=0; i<10;i++){
            MyRunnable myRunnable = new MyRunnable(i);
            executorService.submit(myRunnable);
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中打印出是哪一个线程正在执行任务
 */
class MyRunnable implements Runnable{

    private int id;

    @Override
    public void run() {
//     获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务...."+id);
    }

    public MyRunnable(int id) {
        this.id = id;
    }
}

image-20230426171245502

4.2.3 static ExecutorService newFixedThreadPool(int nThreads)

创建一个可重用固定线程数的线程池,可以规定线程数量

​ 在创建线程池的时候指定线程池中线程的数量,降低服务器的压力。

​ 当我们任务特别多,任务放到缓存中,不会创建更多的线程来执行任务

/**
 * 练习Executors获取ExecutorService,然后调用方法提交任务
 */
public class MyTest02 {
    public static void main(String[] args) {
//      1.使用工厂类获取线程池对象   此时线程池中最多有三个线程
        ExecutorService executorService = Executors.newFixedThreadPool(3);
//      2. 提交任务:
        for (int i=0; i<10;i++){
            MyRunnable02 myRunnable = new MyRunnable02(i);
            executorService.submit(myRunnable);
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中打印出是哪一个线程正在执行任务
 */
class MyRunnable02 implements Runnable{

    private int id;

    @Override
    public void run() {
//     获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务...."+id);
    }

    public MyRunnable02(int id) {
        this.id = id;
    }
}

pool-1-thread-1执行了任务…0
pool-1-thread-3执行了任务…2
pool-1-thread-2执行了任务…1
pool-1-thread-3执行了任务…4
pool-1-thread-1执行了任务…3
pool-1-thread-3执行了任务…6
pool-1-thread-2执行了任务…5
pool-1-thread-3执行了任务…8
pool-1-thread-1执行了任务…7
pool-1-thread-2执行了任务…9

4.2.4 static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)

创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建

​ 与上边那个方法而言,此方法可以控制线程的创建

/**
 * 练习Executors获取ExecutorService,然后调用方法提交任务
 */
public class MyTest02 {
    public static void main(String[] args) {
//      1.使用工厂类获取线程池对象   此时线程池中最多有三个线程
        ExecutorService executorService = Executors.newFixedThreadPool(3, new ThreadFactory() {
            int n=1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义线程名称"+n++);
            }
        });
//      2. 提交任务:
        for (int i=0; i<10;i++){
            MyRunnable02 myRunnable = new MyRunnable02(i);
            executorService.submit(myRunnable);
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中打印出是哪一个线程正在执行任务
 */
class MyRunnable02 implements Runnable{

    private int id;

    @Override
    public void run() {
//     获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务...."+id);
    }

    public MyRunnable02(int id) {
        this.id = id;
    }
}

自定义线程名称3执行了任务…2
自定义线程名称1执行了任务…0
自定义线程名称2执行了任务…1
自定义线程名称3执行了任务…3
自定义线程名称2执行了任务…4
自定义线程名称3执行了任务…6
自定义线程名称2执行了任务…7
自定义线程名称1执行了任务…5
自定义线程名称2执行了任务…9
自定义线程名称3执行了任务…8

4.2.5 static ExecutorService newSingleThreadExecutor()

创建一个使用单个 worker 线程的 Executor,以无界队列方式(任务缓存的时候不限制数量)来运行该线程。

此种方法,只追求安全,不考虑性能

/**
 * 练习Executors获取ExecutorService,然后调用方法提交任务
 */
public class MyTest03 {
    public static void main(String[] args) {
//      1.使用工厂类获取线程池对象
        ExecutorService executorService = Executors.newSingleThreadExecutor();
//      2. 提交任务:
        for (int i=0; i<10;i++){
            MyRunnable03 myRunnable = new MyRunnable03(i);
            executorService.submit(myRunnable);
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中打印出是哪一个线程正在执行任务
 */
class MyRunnable03 implements Runnable{

    private int id;

    @Override
    public void run() {
//     获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务...."+id);
    }

    public MyRunnable03(int id) {
        this.id = id;
    }
}

pool-1-thread-1执行了任务…0
pool-1-thread-1执行了任务…1
pool-1-thread-1执行了任务…2
pool-1-thread-1执行了任务…3
pool-1-thread-1执行了任务…4
pool-1-thread-1执行了任务…5
pool-1-thread-1执行了任务…6
pool-1-thread-1执行了任务…7
pool-1-thread-1执行了任务…8
pool-1-thread-1执行了任务…9

4.2.6 static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

创建一个使用单个 worker 线程的 Executor,且线程池中的所有线程都使用ThreadFactory来创建。

/**
 * 练习Executors获取ExecutorService,然后调用方法提交任务
 */
public class MyTest03 {
    public static void main(String[] args) {
//      1.使用工厂类获取线程池对象
        ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
            int n=1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义线程名称"+n++);
            }
        });
//      2. 提交任务:
        for (int i=0; i<10;i++){
            MyRunnable03 myRunnable = new MyRunnable03(i);
            executorService.submit(myRunnable);
        }
    }
}

/**
 * 任务类:包含一个任务编号,在任务中打印出是哪一个线程正在执行任务
 */
class MyRunnable03 implements Runnable{

    private int id;

    @Override
    public void run() {
//     获取线程的名称,打印一句话
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务...."+id);
    }

    public MyRunnable03(int id) {
        this.id = id;
    }
}

自定义线程名称1执行了任务…0
自定义线程名称1执行了任务…1
自定义线程名称1执行了任务…2
自定义线程名称1执行了任务…3
自定义线程名称1执行了任务…4
自定义线程名称1执行了任务…5
自定义线程名称1执行了任务…6
自定义线程名称1执行了任务…7
自定义线程名称1执行了任务…8
自定义线程名称1执行了任务…9

五、Java内置线程池 - ScheduledExecutorService

ScheduledExecutorService 是 ExecutorService 的子接口

ScheduledExecutorService 具备了延迟运行或定期执行任务的能力

5.1 ScheduledExecutorService 获取

5.1.1 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个可重用固定线程数的线程池且允许延迟运行或定期执行任务.(线程的数量是固定的)

/**
 * 测试ScheduledExecutorService接口中延迟执行任务和重复执行任务功能
 */
public class ScheduledExecutorServiceDemo01 {
    public static void main(String[] args) {
//      1.具备延迟执行任务的线程池对象  (线程池里面最多有三个)
        ScheduledExecutorService es = Executors.newScheduledThreadPool(3);

//      2.创建多个任务对象,提交任务,每个任务延迟执行
//        es.schedule(new MyRunnable(1),2, TimeUnit.SECONDS);
        for (int i = 1; i < 10; i++) {
            es.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS);
        }
//      main方法结束标志
        System.out.println("over");
    }
}

class MyRunnable implements Runnable {

    private int id;

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + "执行了任务编号" + id);
    }

    public MyRunnable(int id) {
        this.id = id;
    }

}

over大概输出2秒后,控制台才输出其他内容

over
pool-1-thread-1执行了任务编号1
pool-1-thread-2执行了任务编号2
pool-1-thread-3执行了任务编号3
pool-1-thread-3执行了任务编号6
pool-1-thread-3执行了任务编号7
pool-1-thread-2执行了任务编号5
pool-1-thread-1执行了任务编号4
pool-1-thread-2执行了任务编号9
pool-1-thread-3执行了任务编号8

5.1.2 static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory)

可以指定线程工厂,从工厂里面得到线程对象

创建一个可重用固定线程数的线程池且线程池中的所有线程都使用ThreadFactory来创建,且允许延迟运行或定期执行任务;

5.1.3 static ScheduledExecutorService newSingleThreadScheduledExecutor()

创建一个单线程执行程序,它允许在给定延迟后运行命令或者定期地执行(单线程,但是允许延期运行、定期运行)

5.1.4 static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory)

可以指定线程工厂,从工厂里面得到线程对象

创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * 测试ScheduledExecutorService接口中延迟执行任务和重复执行任务功能
 */
public class ScheduledExecutorServiceDemo02 {
    public static void main(String[] args) {
//      1.具备延迟执行任务的线程池对象  (线程池里面最多有三个)
        ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
            int num=1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义线程名"+num++);
            }
        });

//      2.创建多个任务对象,提交任务,每个任务延迟执行
        es.scheduleWithFixedDelay(new MyRunnable02(1), 2, 2,TimeUnit.SECONDS);

//      main方法结束标志
        System.out.println("over");
    }
}

class MyRunnable02 implements Runnable { 

    private int id;

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        try {
//          模拟任务的执行时间比较长
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(name + "执行了任务编号" + id);
    }

    public MyRunnable02(int id) {
        this.id = id;
    }

}

5.2 ScheduledExecutorService 常用方法

ScheduledExecutorService常用方法如下

5.2.1 ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit)

​ 延迟时间单位是unit,数量是delay的时间后执行callable。

5.2.2 ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)

​ 延迟时间单位是unit,数量是delay的时间后执行command。

创建一个可重用固定线程数的线程池且允许延迟运行或定期执行任务.(线程的数量是固定的)

/**
 * 测试ScheduledExecutorService接口中延迟执行任务和重复执行任务功能
 */
public class ScheduledExecutorServiceDemo01 {
    public static void main(String[] args) {
//      1.具备延迟执行任务的线程池对象  (线程池里面最多有三个)
        ScheduledExecutorService es = Executors.newScheduledThreadPool(3);

//      2.创建多个任务对象,提交任务,每个任务延迟执行
//        es.schedule(new MyRunnable(1),2, TimeUnit.SECONDS);
        for (int i = 1; i < 10; i++) {
            es.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS);
        }
//      main方法结束标志
        System.out.println("over");
    }
}

class MyRunnable implements Runnable {

    private int id;

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + "执行了任务编号" + id);
    }

    public MyRunnable(int id) {
        this.id = id;
    }

}

over大概输出2秒后,控制台才输出其他内容

over
pool-1-thread-1执行了任务编号1
pool-1-thread-2执行了任务编号2
pool-1-thread-3执行了任务编号3
pool-1-thread-3执行了任务编号6
pool-1-thread-3执行了任务编号7
pool-1-thread-2执行了任务编号5
pool-1-thread-1执行了任务编号4
pool-1-thread-2执行了任务编号9
pool-1-thread-3执行了任务编号8

5.2.3 ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

​ 延迟时间单位是unit,数量是initialDelay的时间后(先延迟initialDelay时间),每间隔period时间重复执行一次command。(第一次开始到第二次开始之间的时间差)

/**
 * 测试ScheduledExecutorService接口中延迟执行任务和重复执行任务功能
 */
public class ScheduledExecutorServiceDemo01 {
    public static void main(String[] args) {
//      1.具备延迟执行任务的线程池对象  (线程池里面最多有三个)
        ScheduledExecutorService es = Executors.newScheduledThreadPool(3, new ThreadFactory() {
            int num=1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义线程名"+num++);
            }
        });

//      2.创建多个任务对象,提交任务,每个任务延迟执行
            es.scheduleAtFixedRate(new MyRunnable(1), 2, 2,TimeUnit.SECONDS);

//      main方法结束标志
        System.out.println("over");
    }
}

class MyRunnable implements Runnable {

    private int id;

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        try {
//          模拟任务的执行时间比较长
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(name + "执行了任务编号" + id);
    }

    public MyRunnable(int id) {
        this.id = id;
    }

}

over
自定义线程名1执行了任务编号1
自定义线程名1执行了任务编号1
自定义线程名2执行了任务编号1
自定义线程名2执行了任务编号1
自定义线程名2执行了任务编号1
自定义线程名2执行了任务编号1
自定义线程名2执行了任务编号1
自定义线程名2执行了任务编号1
自定义线程名2执行了任务编号1

5.2.4 ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

​ 创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟。(第一次结束到下一次开始之间的时间差)

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

/**
 * 测试ScheduledExecutorService接口中延迟执行任务和重复执行任务功能
 */
public class ScheduledExecutorServiceDemo02 {
    public static void main(String[] args) {
//      1.具备延迟执行任务的线程池对象  (线程池里面最多有三个)
        ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
            int num=1;
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r,"自定义线程名"+num++);
            }
        });

//      2.创建多个任务对象,提交任务,每个任务延迟执行
        es.scheduleWithFixedDelay(new MyRunnable02(1), 2, 2,TimeUnit.SECONDS);

//      main方法结束标志
        System.out.println("over");
    }
}

class MyRunnable02 implements Runnable { 

    private int id;

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        try {
//          模拟任务的执行时间比较长
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(name + "执行了任务编号" + id);
    }

    public MyRunnable02(int id) {
        this.id = id;
    }

}

六、 Future - 异步计算结果

​ 刚刚在java内置线程池使用时,没有考虑线程计算的结果,但开发中有时需要利用线程进行一些计算,然后获取这些计算的结果,而java中的Future接口就是专门用于描述异步计算结果的,我们可以通过Future对象获取线程计算的结果。

​ 在业务中用多线程计算某个任务,但是必须等待线程的执行结果才能往下进行(要使用带返回值的线程,否则怎么样也获取不到某些结果)。

6.1 常用方法

boolean cancel(boolean maylnterruptlfRunning)

​ 试图取消对此任务的执行。(任务正在执行或者还没有执行的时候 )

V get ()
如有必要,等待计算完成,然后获取其结果

V get(long timeout, TimeUnit unit)
如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)

boolean isCancelled()
如果在任务正常完成前将其取消,则返回 true。

boolean isDone()
如果任务已完成,则返回 true。

6.2 Future演示

**
 *   练习异步计算结果
 */
public class FutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
//      1. 获取一个线程池对象
        ExecutorService es = Executors.newCachedThreadPool();

//      2. 创建Callable类型的任务对象
        Future<Integer> submit = es.submit(new MyCall(2, 5));

//      3.  判断任务是否已经完成
        boolean done = submit.isDone();
        System.out.println("第一次判断任务是否完成:"+done);

//      4.判断任务是否已经被取消
        boolean cancelled = submit.isCancelled();
        System.out.println("第一次判断任务是否取消:"+cancelled);

//      5.无限期等待结果,  直到完成为止
        Integer integer = submit.get();
        System.out.println("任务执行的结果是:"+integer);

         done = submit.isDone();
        System.out.println("第二次判断任务是否完成:"+done);

        cancelled = submit.isCancelled();
        System.out.println("第二次判断任务是否取消:"+cancelled);

//        *************************************
    }
}

class MyCall implements Callable<Integer>{

    private  int a;
    private  int b;


    @Override
    public Integer call() throws Exception {
        String name = Thread.currentThread().getName();
        System.out.println(name+"准备开始....");
        Thread.sleep(2000);
        System.out.println(name+"计算完成....");
        return a+b;
    }

    public MyCall(int a, int b) {
        this.a = a;
        this.b = b;
    }
}

七、 综合案例

7.1 案例

案例介绍:

​ 假如某网上商城推出活动,新上架10部新手机免费送客户体验,要求所有参与活动的人员在规定的时间同时参与秒杀挣抢假如有20人同时参与了该活动,请使用线程池模拟这个场景,保证前10人秒杀成功,后10人秒杀失败

要求:

​ 1:使用线程池创建线程
​ 2:解决线程安全问题

思路提示:

​ 1:既然商品总数量是10个,那么我们可以在创建线程池的时候初始化线程数是10个及以下,设计线程池最大数量为10个

​ 2:当某个线程执行完任务之后,可以让其他秒杀的人继续使用该线程参与秒杀;

​ 3:使用svnchronized控制线程安全防止出现错误数据

代码步骤:

​ 1:编写任务类,主要是送出手机给秒杀成功的客户
​ 2:编写主程序类创建20个任务(模拟20个客户);
​ 3.创建线程池对象并接收20个任务开始执行任务

7.2 代码

public class MyTest {
    public static void main(String[] args) {
//       1. 创建一个线程池对象
//         核心线程3,最大线程数量5 ,最大空闲时间1分钟,任务队列里面最多缓存15个任务
        ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<>(15));

//      2.创建任务对象
        for (int i = 1; i <= 20; i++) {
            MyTask myTask = new MyTask("客户"+i);
            pool.submit(myTask);
        }

//      3.关闭线程池
        pool.shutdown();
    }
}
/**
 * 任务类:
 *    包含商品数量,客户名称,送手机的行为
 */
public class MyTask implements Runnable{

//  设计一个变量,用于表示商品的数量
    private static int num=10;

//  客户名称
    private String userName;

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(userName+"正在使用"+name+"参与秒杀任务......");

        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

//      使用字节码文件作为锁对象
        synchronized (MyTask.class){
             if (num>0){
                 System.out.println(userName+"正在使用"+name+"秒杀了"+ --num +"商品");
             }else {
                 System.out.println(userName+"秒杀失败");
             }
        }
    }

    public MyTask(String userName) {
        this.userName = userName;
    }
}

客户2正在使用pool-1-thread-2参与秒杀任务…
客户20正在使用pool-1-thread-5参与秒杀任务…
客户3正在使用pool-1-thread-3参与秒杀任务…
客户19正在使用pool-1-thread-4参与秒杀任务…
客户1正在使用pool-1-thread-1参与秒杀任务…
客户3正在使用pool-1-thread-3秒杀了9商品
客户20正在使用pool-1-thread-5秒杀了8商品
客户19正在使用pool-1-thread-4秒杀了7商品
客户4正在使用pool-1-thread-5参与秒杀任务…
客户5正在使用pool-1-thread-4参与秒杀任务…
客户6正在使用pool-1-thread-3参与秒杀任务…
客户1正在使用pool-1-thread-1秒杀了6商品
客户2正在使用pool-1-thread-2秒杀了5商品
客户7正在使用pool-1-thread-1参与秒杀任务…
客户8正在使用pool-1-thread-2参与秒杀任务…
客户8正在使用pool-1-thread-2秒杀了4商品
客户7正在使用pool-1-thread-1秒杀了3商品
客户6正在使用pool-1-thread-3秒杀了2商品
客户5正在使用pool-1-thread-4秒杀了1商品
客户10正在使用pool-1-thread-1参与秒杀任务…
客户9正在使用pool-1-thread-2参与秒杀任务…
客户12正在使用pool-1-thread-4参与秒杀任务…
客户4正在使用pool-1-thread-5秒杀了0商品
客户11正在使用pool-1-thread-3参与秒杀任务…
客户13正在使用pool-1-thread-5参与秒杀任务…
客户11秒杀失败
客户10秒杀失败
客户14正在使用pool-1-thread-3参与秒杀任务…
客户15正在使用pool-1-thread-1参与秒杀任务…
客户9秒杀失败
客户12秒杀失败
客户16正在使用pool-1-thread-2参与秒杀任务…
客户17正在使用pool-1-thread-4参与秒杀任务…
客户13秒杀失败
客户18正在使用pool-1-thread-5参与秒杀任务…
客户17秒杀失败
客户15秒杀失败
客户16秒杀失败
客户14秒杀失败
客户18秒杀失败

使用字节码文件作为锁对象
synchronized (MyTask.class){
if (num>0){
System.out.println(userName+“正在使用”+name+“秒杀了”+ --num +“商品”);
}else {
System.out.println(userName+“秒杀失败”);
}
}
}

public MyTask(String userName) {
    this.userName = userName;
}

}




> 客户2正在使用pool-1-thread-2参与秒杀任务......
> 客户20正在使用pool-1-thread-5参与秒杀任务......
> 客户3正在使用pool-1-thread-3参与秒杀任务......
> 客户19正在使用pool-1-thread-4参与秒杀任务......
> 客户1正在使用pool-1-thread-1参与秒杀任务......
> 客户3正在使用pool-1-thread-3秒杀了9商品
> 客户20正在使用pool-1-thread-5秒杀了8商品
> 客户19正在使用pool-1-thread-4秒杀了7商品
> 客户4正在使用pool-1-thread-5参与秒杀任务......
> 客户5正在使用pool-1-thread-4参与秒杀任务......
> 客户6正在使用pool-1-thread-3参与秒杀任务......
> 客户1正在使用pool-1-thread-1秒杀了6商品
> 客户2正在使用pool-1-thread-2秒杀了5商品
> 客户7正在使用pool-1-thread-1参与秒杀任务......
> 客户8正在使用pool-1-thread-2参与秒杀任务......
> 客户8正在使用pool-1-thread-2秒杀了4商品
> 客户7正在使用pool-1-thread-1秒杀了3商品
> 客户6正在使用pool-1-thread-3秒杀了2商品
> 客户5正在使用pool-1-thread-4秒杀了1商品
> 客户10正在使用pool-1-thread-1参与秒杀任务......
> 客户9正在使用pool-1-thread-2参与秒杀任务......
> 客户12正在使用pool-1-thread-4参与秒杀任务......
> 客户4正在使用pool-1-thread-5秒杀了0商品
> 客户11正在使用pool-1-thread-3参与秒杀任务......
> 客户13正在使用pool-1-thread-5参与秒杀任务......
> 客户11秒杀失败
> 客户10秒杀失败
> 客户14正在使用pool-1-thread-3参与秒杀任务......
> 客户15正在使用pool-1-thread-1参与秒杀任务......
> 客户9秒杀失败
> 客户12秒杀失败
> 客户16正在使用pool-1-thread-2参与秒杀任务......
> 客户17正在使用pool-1-thread-4参与秒杀任务......
> 客户13秒杀失败
> 客户18正在使用pool-1-thread-5参与秒杀任务......
> 客户17秒杀失败
> 客户15秒杀失败
> 客户16秒杀失败
> 客户14秒杀失败
> 客户18秒杀失败





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

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

相关文章

假期后,野兔百科系统网站源码新版更新发布

这个是野兔百科系统中文版更新&#xff0c;这次更新了增加几个功能模块&#xff0c;几个已知的问题&#xff0c;修复系统部分功能。 系统名称&#xff1a;野兔百科系统 系统语言&#xff1a;中文版 系统源码&#xff1a;不加密&#xff0c;开源 系统开发&#xff1a;PHPMySQL …

尚融宝29-提现和还款

目录 一、提现 &#xff08;一&#xff09;需求 &#xff08;二&#xff09;前端 &#xff08;三&#xff09;后端 1、提现接口 2、回调接口 二、还款 &#xff08;一&#xff09;需求 &#xff08;二&#xff09;前端 &#xff08;三&#xff09;后端 1、还款接口 …

第一章:概述

1&#xff0c;因特网概述 1.网络、互联网和英特网 网络(Network)由若干结点(Node)和连接这些结点的链路(Link)组成。 多个网络还可以通过路由器互连起来&#xff0c;这样就构成了一个覆盖范围更大的网络&#xff0c;即互联网(或互连网)。因此&#xff0c;互联网是“网络的网络…

UE蓝图基础学习笔记(未完待续2023/05/03)

文章目录 一、项目创建1&#xff09;准备流程&#xff08;选择模板、开发语言、平台、质量等&#xff09;2&#xff09;界面介绍 二、Actor三、操作关卡对象&#xff08;旋转、移动、缩放和坐标轴&#xff09;四、常用快捷键五、运行游戏六、蓝图介绍七、蓝图节点八、操作事件图…

Azure DevOps Server 2022.0.1升级手册

Contents 1. 概述2. 操作方法 2.1 安装操作系统2.2 安装数据库2.4 还原数据2.3 安装和配置Azure DevOps Server 1. 概述 Azure DevOps Server 是微软公司经过20多年的持续开发&#xff0c;逐渐将需求管理、敏捷实践、源代码管理、持续集成等功能集成一体&#xff0c;实现应用软…

AutoHotKey简单入门

简单入门 快捷键 ^j::Send, Hello world! Return^j::代表CtrlJ&#xff0c;其中^代表Ctrl键 Send命令&#xff1a;在光标处输入Hello world! 也就是说&#xff0c;你按下CtrlJ后&#xff0c;将会输入字符串Hello world! Return即返回 热字串 ::ftw::Free the whales Ret…

抖音营销策略:新手如何利用抖音提高品牌曝光度

随着短规频平台的兴起&#xff0c;抖音作为其中的校佼者&#xff0c;已经成为了众多用户和企业的营销利器。但是&#xff0c;对于抖音新手而言&#xff0c;如何在这个平台上快速提升影响力呢?下面不若与众就为大家分享几个实用的方法。 一、关注抖音热门话题和潮流 抖音平台上…

力扣题库刷题笔记581-最短无序连续子数组

1、题目如下&#xff1a; 2、题解代码实现&#xff1a; 浅看题解&#xff0c;解题思路和本人接替思路一毛一样&#xff0c;奈何没有想到用双指针&#xff0c;在代码实现上也存在问题。当知道用双指针的时候&#xff0c;本题也变得相对简单。思路如下&#xff1a; a、输入仅存在…

Vue条件渲染v-if和v-show

条件渲染v-if和v-show <div id"root"><!-- <div v-if"true">v-if</div>--> <!-- <div v-show"true">v-show</div>--> n:{{n}}<button click"n">点击n</button><div v…

法规标准-UN R152标准解读

UN R152是做什么的&#xff1f; UN R152 全名为关于M1和N1型机动车高级紧急制动系统&#xff08;AEBS&#xff09;型式认证的统一规定&#xff0c;是联合国对于M1和N1型车辆AEBS系统认证的要求说明&#xff0c;当满足其要求内容时&#xff0c;才可通过联合国的认证&#xff0c…

数字化转型导师坚鹏:面向数字化转型的大数据顶层设计实践

面向数字化转型的大数据顶层设计实践 课程背景&#xff1a; 数字化背景下&#xff0c;很多企业存在以下问题&#xff1a; 不清楚大数据思维如何建立&#xff1f; 不清楚企业大数据分析方法&#xff1f; 不了解大数据应用成功案例&#xff1f; 课程特色&#xff1a; …

(转载)01.Matplotlib 图像结构-figure()axes设置

​概要&#xff1a;介绍matplotlib 绘制图像起手&#xff0c; figure() 的设置&#xff0c; axes() 的设置。主要的内容可移步最后部分的总结。 04 Matplotlib 总结 Matplotlib 提供了matplotlib.figure图形类模块&#xff0c;它包含了创建图形对象的方法。通过调用 pyplot 模…

Hive3面试基础

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、基本知识Hive31.表的类型和表的存储格式a)b)c)创建表i&#xff09;ii&#xff09; 2.表 二、使用步骤1.引入库2.读入数据 总结 前言 面试准备之Hive 回顾…

个人代码管理

项目描述&#xff1a; 在公司使用软件大家会经常使用GitLab进行代码管理&#xff0c;但是GitLab对于个人使用会有&#xff0c;操作相对复杂&#xff0c;且需要收费。GitHub的代码又都是开放的。经过上网查找和尝试&#xff0c;找到了一个可以日常用来保存自己代码的工具。&…

吴恩达和OpenAI的《面向开发者的ChatGPT提示工程》精华笔记

《ChatGPT Prompt Engineering for Developers》 面向开发者的ChatGPT提示工程 shadow 趁着假期&#xff0c;学习了prompt课程&#xff0c;做了一些精简和关键知识点的梳理&#xff0c;分享给大家。 LLM 可完成的任务 包括: 总结&#xff08;如总结用户评论&#xff09; 推断&a…

streamlit+pywebview,纯python以前后端形式写桌面应用

1、VSCode VSCode VSCode扩展&#xff1a;Python 2、配置PowerShell执行策略 以管理员身份运行PowerShell&#xff0c;运行Set-ExecutionPolicy RemoteSigned&#xff0c;并输入Y&#xff0c;回车确认 3、配置Python环境 只安装Python&#xff1a;华为镜像、阿里镜像、new…

DC-DC直流隔离升压高压输出电源模块12v24v48v转50V110V200V250V350V400V500V600V800V1000V微功率

特点 效率高达 80%以上1*2英寸标准封装单电压输出价格低稳压输出工作温度: -40℃~85℃阻燃封装&#xff0c;满足UL94-V0 要求温度特性好可直接焊在PCB 上 应用 HRB W2~40W 系列模块电源是一种DC-DC升压变换器。该模块电源的输入电压分为&#xff1a;4.5~9V、9~18V、及18~36V、…

Android 编译模块 (小记)

1.整编 source build/envbuild.sh lunch xxx make 2.单独编译模块 2.1 去Android.bp 中找模块名 比如我想编译system/core/fastboot&#xff0c;那么我就去找这个路径下的Android.bp/ Android.mk Android.bp 中找每个模块的那么就行 然后直接make这个name make fastboot_…

通过【Stable-Diffusion WEBUI】复刻属于你的女神:再谈模型与参数

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;复刻优秀的作品&#xff08;1.1&#xff09;模型&#xff08;1.1.1&#xff09;Model Hash &#xff08;1.2&#xff09;提示词&#xff08;1.2.1&#xff09;反推提示词 &#xff08;1.3&#xff09;参数 &…

GaussDB数据库基础函数介绍-上

目录 一、函数在数据库中的作用 二、GaussDB常用基础函数介绍与示例 1、数字操作函数 2、时间和日期处理函数 3、类型转换函数 4、数组函数 5、范围函数 6、窗口函数 7、聚集函数 8、安全函数 9、系统信息函数 10、动态脱敏函数. Tip&#xff1a;由于篇幅缘故&…