• 多线程-定时器、线程池


    定时器

    到一定时间就执行一个准备好的代码/方法。注册一个任务;任务会在指定时间进行执行。

    标准库的使用

    定时器:指定一个时间去执行一个任务,让程序去代替人工准时操作。

    import java.util.Timer;
    import java.util.TimerTask;
    
    public class test18 {//标准库定时器的使用
        public static void main(String[] args) {
            System.out.println("任务启动前");
            Timer timer=new Timer();//有很多方法;参数。可以注册多个任务。也得看官方文档
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    System.out.println("任务启动后");
                }
            }, 3000);
    
            }
    
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    线程池使用

    并发量的提升我们会发现;线程的创建也会消耗不少资源;使用我们使用线程池降低线程的创建和销毁开销。因为线程池是事先就将线程创建好放入"线程池"中,后面需要的时候,直接从池子中取。这样就省去线程的创建和销毁开销;省一些CPU。

    先看一下标准库的线程池怎么使用:

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    // 使用一下标准库的线程池
    public class ThreadDemo26 {
        public static void main(String[] args) {
            ExecutorService pool = Executors.newFixedThreadPool(10);
            for (int i = 0; i < 1000; i++) {
                int n = i;
                pool.submit(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("hello " + n);
                    }
                });
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    ExecutorService pool = Executors.newFixedThreadPool(10);相当于new操作;这个操作很奇葩;用某个类的静态方法直接构造出一个对象来(相当于把new操作隐藏到方法后面)
    工厂模式:上述的这种设计模式;用普通方法代替构造方法创建对象;但是有坑;如果创建多种对象;构造不同情况对象;多个构造方法得通过重载方式创建
    submit方法:给线程池提交任务;线程池的线程都是前台线程;会阻止进程结束。定时器也是如此

    有个问题:
    在这里插入图片描述

    lanbda表达式;这里的run方法属于Runnable;这个方法的执行时机是在未来的某个节点;后续线程池队列中;排到他就让对应的线程去执行。这个for循环的i是主线程的局部变量;主线程执行完就销毁了;run任务还没在线程池排到队;i就已经销毁。

    变量捕获:内部类(Runnable的匿名内部类)捕获了外部方法中的局部变量n。
    匿名内部类(包括Lambda表达式)可以访问它们周围方法的局部变量,n是一个局部变量,但它是final的,因为它在for循环内部不会被修改。
    jdk1.8之前只能捕获final;而1.8后无需显式声明它们为final;只要代码没修改这个变量就可以捕获。
    n是在每次迭代中都创建了一个新的变量实例,而i是在不同迭代中都指向相同的变量实例。所以i是没法捕获。

    上述线程池的使用;就是一个工厂类;包装ThreadPooIExecutor实现的;让我们使用更方便。
    在这里插入图片描述
    ThreadPoolExecutor提供了更多的可选参数,可以进一步细化线程池行为的设定;后续再详细介绍;以及自定义的定时器、线程池的实现。

    线程数目确定

    1:CPU每个线程要执行的任务都是狂转CPU(进行一系列算术运算)此时线程池线程数多也不应该超过 CPU 核数
    2:IO密集型每个线程干的工作就是等待 0(读写硬盘,读写网卡,等待用户输入…)不吃 CPU此时这样的线程处于阻塞状态不参与 CPU 调度;你就可以设置多一点
    实际中:实践出真知;通过测试/实验的方式;看看哪个效果最好

  • 相关阅读:
    如何防止数据库泄露,保障个人信息安全
    《操作系统真象还原》第一章 部署工作环境
    Flask + Bootstrap vs Flask + React/Vue:初学者指南
    C++下基于std标准库实现配置文件的读取
    024 - STM32学习笔记 - 液晶屏控制(一) - LTDC与DMA2D初始
    docker-compose搭建redis环境:哨兵模式(一主两重两哨兵)
    从0开始学习JavaScript--JavaScript 字符串与文本内容使用
    文件包含漏洞总结
    【23真题】很少见!第6题有点新颖!
    Linux日志管理rsyslog系统日志管理
  • 原文地址:https://blog.csdn.net/m0_64254651/article/details/128757334