微服务 第一章 Java线程池技术应用

2023-09-14 10:23:47

系列文章目录

第一章 Java线程池技术应用

在这里插入图片描述


前言

介绍Java的线程、线程池等操作

1、Java创建线程方式回顾

1.1、继承Thread类(只运行一次)

public class ThreadTest extends Thread{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        new ThreadTest().start();
    }
}

1.1.1、改造成主线程常驻,每秒开启新线程运行


import java.util.Date;

@Slf4j
public class ThreadTest extends  Thread{

    @Override
    public void run() {
        log.info("线程名称:{} , 当前时间:{}" , Thread.currentThread().getName() , new Date().getTime() );
    }

    public static void main(String[] args) {

        while (true) {
            try {
                new ThreadTest().start();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.info("主线程常驻");
        }
    }

}

1.1.2、匿名内部类

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ThreadTest extends  Thread{

    public static void main(String[] args) {

        Thread thread = new Thread() {
            @Override
            public void run() {
                log.info("Hello {}" , "world");
            }
        };

        thread.start();
    }

}

1.1.3、缺点

继承了Thread类之后,就不能继承其他类

1.1.4、扩展知识:Java内部类

成员内部类(外部类内部使用,外部类外部使用)


import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Outer {
    public static void main(String[] args) {
        Inner inner = new Outer().initTest();
        log.info(inner.innerTest());
    }

    public Inner initTest(){
        Inner inner = new Inner();
        return  inner;
    }

    class Inner{
        public Inner(){

        }
        public Inner(String s){

        }

        public String innerTest(){
            return "Inner Hello world";
        }
    }
}

1.1.4.1、静态内部类

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Outer {
    public static void main(String[] args) {
        Inner inner = new Inner();
        log.info(inner.innerTest());
    }

    public void initTest(){
        Inner inner = new Inner();
    }

    static class Inner{
        public Inner(){

        }
        public Inner(String s){

        }
        public String innerTest(){
            return "Inner Hello world";
        }
    }
}

1.1.4.2、匿名内部类

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Outer {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.sayHello();

    }
    public void sayHello(){
        IMessage iMessage = new IMessage() {
            //匿名类
            @Override
            public String sayHello() {
                return "Hello world";
            }
        };
        log.info(iMessage.sayHello());

    }

    interface IMessage{
        String sayHello();
    }

}

1.2、实现Runnable接口

1.2.1、普通类实现Runnable接口

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class StatSales implements Runnable{
    @Override
    public void run() {
        log.info("统计销量");
    }

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                log.info("Hello world");
            }
        };
        thread.start();
    }
}

1.2.2、匿名方式创建Runnable实现类

public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("统计成绩");
            }
        };
        new Thread(runnable).start();
    }

1.2.3、使用Lambda方式创建匿名Runnable类

// 使用 Lambda 匿名 Runnable 方式
Thread t3 = new Thread(() -> {
//添加业务方法…
});
// 启动线程
t3.start();

public static void main(String[] args) {
    Thread thread = new Thread(() -> {
        System.out.println("统计平均寿命");
    });

    thread.start();
}

1.2.4、缺点

不能获得程序的执行结果

1.2.5、扩展Lambda表达式

把函数作为一个方法的参数
表达式语法:

(parameters) -> expression

(parameters) ->{ statements; }

说明:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
  • 举例:

    // 1. 不需要参数,返回值为 5  
    () -> 5  
      
    // 2. 接收一个参数(数字类型),返回其2倍的值  
    x -> 2 * x  
      
    // 3. 接受2个参数(数字),并返回他们的差值  
    (x, y) -> x – y  
      
    // 4. 接收2个int型整数,返回他们的和  
    (int x, int y) -> x + y  
      
    // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
    (String s) -> System.out.print(s)
    

    1.3、实现Callable接口

    FutureTask+Callable

    1.3.1、普通类实现Callable接口

    public class StatScore implements Callable<Double> {
        @Override
        public Double call() throws Exception {
            //统计分数的逻辑
            return 88.98;
        }
    }
    
     public static void main(String[] args) throws ExecutionException, InterruptedException {
            StatScore statScore = new StatScore();
            //跟FutureTask 关联上
            FutureTask<Double> doubleFutureTask = new FutureTask<>(statScore);
            //跟Thread关联上
            Thread thread = new Thread(doubleFutureTask);
            thread.start();
            log.info(String.valueOf(doubleFutureTask.get()));
        }
    

    2、线程池

    线程池就是存放线程的池子,池子里存放了很多可以复用的线程。
    使用线程池的优势

  • 提高效率,创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
  • 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  • 提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;
  • 在这里插入图片描述

    2.1、五种创建线程的方式

    //创建一个单线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    //创建一个定长的线程池,可控制最大并发数,超出的线程进行队列等待。 ExecutorService executorService =
    Executors.newFixedThreadPool(2); //可以创建定长的、支持定时任务,周期任务执行。
    ExecutorService executorService = Executors.newScheduledThreadPool(2);
    //创建一个可以缓存的线程池,如果线程池长度超过处理需要,可以灵活回收空闲线程,没回收的话就新建线程 ExecutorService
    executorService = Executors.newCachedThreadPool(); //创建一个具有抢占式操作的线程池
    ExecutorService executorService = Executors.newWorkStealingPool();

    2.2、new ThreadPoolExecutor()创建线程

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

    参数说明:

    参数含义解释
    corePoolSize该线程池中核心线程数最大值核心线程生命周期无限,即使空闲也不会死亡
    maximumPoolSize线程总数最大值任务队列满了以后当有新任务进来则会增加一个线程来处理新任务(线程总数<maximumPoolSize )
    keepAliveTime闲置超时时间当线程数大于核心线程数时,超过keepAliveTime时间将会回收非核心线程
    unitkeepAliveTime 的单位
    workQueue线程池中的任务队列*
    threadFactory为线程池提供创建新线程的线程工厂*
    RejectedExecutionHandler饱和策略抛出异常专用,当队列和最大线程池都满了之后的饱和策略。

    在这里插入图片描述

    2.2.1、拒绝策略

    ThreadPoolExecutor的饱和策略可以通过调用setRejectedExecutionHandler来修改。JDK提供了几种不同的RejectedExecutionHandler实现,每种实现都包含有不同的饱和策略:AbortPolicy、CallerRunsPolicy、DiscardPolicy和DiscardOldestPolicy。

    拒绝策略如下:

  • CallerRunsPolicy : 调用线程处理任务
  • AbortPolicy : 抛出异常
  • DiscardPolicy : 直接丢弃
  • DiscardOldestPolicy : 丢弃队列中最老的任务,执行新任务
  •         RejectedExecutionHandler rejected = null;
    
            //默认策略,阻塞队列满,则丢任务、抛出异常
            rejected = new ThreadPoolExecutor.AbortPolicy();
    
            //阻塞队列满,则丢任务,不抛异常
            rejected = new ThreadPoolExecutor.DiscardPolicy();
    
            //删除队列中最旧的任务(最早进入队列的任务),尝试重新提交新的任务
            rejected = new ThreadPoolExecutor.DiscardOldestPolicy();
    
            //队列满,不丢任务,不抛异常,若添加到线程池失败,那么主线程会自己去执行该任务
            rejected = new ThreadPoolExecutor.CallerRunsPolicy();
            
    

    总结: 就是被拒绝的任务,直接在主线程中运行,不再进入线程池。

更多推荐

【C++从入门到精通】第2篇:C++基础知识(中)

文章目录2.1iostream介绍:cout、cin和endl2.1.1输入/输出库2.1.2std::cout2.1.3std::endl2.1.4std::cout是缓冲的2.1.5std::endl与\n2.1.6std::cin2.1.7总结2.1.8练习时间2.2未初始化的变量和未定义的行为2.2.1未初始化

Camera Tunning ISP 模块面试总结

一.ISP的调试流程概述:在ISP调试流程中,我们首先需要确认以下三个方面:项目需求、硬件问题确认和Sensor驱动配置确认。项目需求方面,即Sensor需要出多大的分辨率去调效果;因为有些芯片有最大分辨率支持的限制,可能需要调整sensor输出的分辨率大小;而且要注意下减小分辨率后,输出图像的fov及像素的长宽比,减

泡泡玛特加速海外布局,泰国首店开业吸引超千名粉丝排队

自2022年起,泡泡玛特全球门店布局加速,在包括英国、美国、新西兰欧美国家均开设新店面,2022年7月,泡泡玛特全球首家旗舰店落地首尔。泡泡玛特自2018年年底开始规划出海,截至目前,在全球已经拥有50多家门店,2023年以来,法国巴黎、澳大利亚悉尼、马来西亚吉隆坡等持续开店,覆盖跨境电商与本地电商等线上渠道,一季度海

网络安全:保护你的系统

🌷🍁博主猫头虎(🐅🐾)带您GotoNewWorld✨🍁🦄博客首页——🐅🐾猫头虎的博客🎐🐳《面试题大全专栏》🦕文章图文并茂🦖生动形象🐅简单易学!欢迎大家来踩踩~🌺🌊《IDEA开发秘籍专栏》🐾学会IDEA常用操作,工作效率翻倍~💐🌊《100天精通Golang(基础入门篇)》🐅学会Gol

新概念英语(第二册)复习——Lesson 1 - Lesson5

前言重新整理下自己走过的英语路,算是一次梳理,也是一次简单的常识,更是为了以后做一次备份不用怀疑,学完我曾经看过,听过,背过的资料,你也可以成为英语高手。文章目录前言Lesson1-Aprivateconversation原文翻译单词Lesson2-Breakfastorlunch?原文译文单词Lesson3-Plea

rust字符串

标准库提供了String结构体表示字符串。String实际上就是Vec<u8>的封装。唯一的不同是String的方法假定Vec<u8>中的二进制都是utf8编码的pubstructString{vec:Vec<u8>,}一、定义String1.使用new方法创建空字符串letstring=String::new();2

前端深入理解JavaScript面向对象编程与Class

🎬岸边的风:个人主页🔥个人专栏:《VUE》《javaScript》⛺️生活的理想,就是为了理想的生活!目录引言1.什么是面向对象编程?2.Class的基本概念3.Class的语法3.1构造函数3.2属性3.3方法3.4方法的访问修饰符4.类的静态方法和属性5.Getter和Setter方法6.类的私有属性和方法7.

深度学习-Python调用ONNX模型

目录ONNX模型使用流程获取ONNX模型方法使用ONNX模型手动编写ONNX模型Python调用ONNX模型常见错误错误raiseValueError...:错误:Loadmodelmodel.onnxfailed错误:'CUDAExecutionProvider'isnotinavailableprovider错误:

GFS 分布式文件系统

1、GlusterFS概述1.1GlusterFS简介GlusterFS是一个开源的分布式文件系统。由存储服务器、客户端以及NFS/Samba存储网关(可选,根据需要选择使用)组成。没有元数据服务器组件,这有助于提升整个系统的性能、可靠性和稳定性。MFS传统的分布式文件系统大多通过元服务器来存储元数据,元数据包含存储节

前端实现websocket的应用场景以及逻辑实现

前端实现websocket的应用场景以及逻辑实现前端在基础业务逻辑外,根据具体的业务需求还可以实现更复杂的交互逻辑,如:数据同步:WebSocket可用于实时更新数据,当服务器端数据发生变化时,通过WebSocket将变化的数据推送给前端,以保持数据的实时同步。聊天功能:使用WebSocket实现实时聊天功能,前端用户

Guava精讲(三)-Caches,同步DB数据到缓存

在开发中,我们经常需要从数据库中读取数据并进行频繁的读取操作。缓存在各种场景中都有运用,例如,当一个值的计算或检索成本很高,而且在某个输入中需要多次使用该值时,就应该考虑使用缓存,因此将数据缓存在内存中可以显著提高应用程序的性能。问题描述假设我们正在开发一个电子商务网站,需要频繁地显示商品信息。商品信息存储在数据库中,

热文推荐