美文网首页
多线程基础-7种创建线程方式-手写@Async异步注解

多线程基础-7种创建线程方式-手写@Async异步注解

作者: 弹钢琴的崽崽 | 来源:发表于2021-07-06 22:08 被阅读0次

    一. 多线程的基本概念

    1. 什么是cpu

    CPU的中文名称是中央处理器,是进行逻辑运算用的,主要由运算器、控制器、寄存器三部分组成,从字面意思看就是运算就是起着运算的作用,控制器就是负责发出cpu每条指令所需要的信息,寄存器就是保存运算或者指令的一些临时文件,这样可以保证更高的速度。

    也就是我们的线程运行在cpu之上。

    CPU

    2. 什么是线程/进程

    进程是资源分配最小单位,线程是程序执行的最小单位。 计算机在执行程序时,会为程序创建相应的进程,进行资源分配时,是以进程为单位进行相应的分配。每个进程都有相应的线程,在执行程序时,实际上是执行相应的一系列线程。

    总结:进程是资源分配最小单位,线程是程序执行的最小单位

    什么是进程:

    1. cpu从硬盘中读取一段程序到内存中,该执行程序的实例就叫做进程

    2. 一个程序如果被cpu多次被读取到内存中,则变成多个独立的进程

    什么是线程:

    线程是程序执行的最小单位,在一个进程中可以有多个不同的线程

    同时执行。

    3. 为什么在进程中还需要线程呢?

    同一个应用程序中(进程),更好并行处理。

    4. 为什么需要使用到多线程

    采用多线程的形式执行代码,目的就是为了提高程序的效率。

    目的就是为了提高程序开发的效率

    比如:现在一个项目只有一个程序员开发,需要开发功能模块会员模块、支付模块、订单模块。

    5. 并行/串行区别

    串行也就是单线程执行 代码执行效率非常低,代码从上向下执行;

    并行就是多个线程并行一起执行,效率比较高。

    6. 使用多线程一定提高效率吗?

    多线程 执行 需要同时执行

    不一定,需要了解cpu调度的算法

    就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务

    如果生产环境中开启几百个或者上千个线程,而我们的服务器核数8核

    16核 32核,这么多线程都会在我们这些cpu上做上下文切换

    上下文切换:

    从该线程执行切换到另外的线程 该线程---运行切换为就绪状态。

    线程池:和服务器cpu核数 8核 16核

    7. 同步与异步的区别

    同步概念:就是代码从上向下执行。

    异步的概念:单独分支执行 相互之间没有任何影响。

    8. CPU调度时间片

    1. 单核的cpu上每次只能够执行一次线程,如果在单核的cpu上开启了多线程,则会发生对每个线程轮流执行 。
    2. Cpu每次单个计算的时间成为一个cpu时间片,实际只有几十毫秒人为感觉好像是在多线程。
    3. 对于线程来说,存在等待cpu调度的时候 该线程的状态是为就绪状态,如果被cpu调度则该线程的状态为运行状态
    4. 当cpu转让执行其他的线程时,则该线程有变为就绪状态。

    如果在单核的cpu之上开启了多线程,底层执行并不是真正意义上的多线程。

    利用多核多线程性能。

    9. Cpu密集型/IO密集型

    Cpu密集型:长时间占用cpu;例如: 视频剪辑

    IO密集型 :cpu计算时间短 访问外接设备时间长

    Input/output

    10. CPU调度算法原理

    1. 先来先服务 缺点如果最先来执行的线程 是CPU密集型 这样话可能会一直无法继续执行其他的线程。
    2. 最短作业法 谁的计算时间短,谁先来执行。
    3. 优先级调度算法 根据重要性将进程分成4个优先级

    优先级4 进程D负责画面----

    优先级3 进程B和进程C接受用户的指令 重要

    优先级2 后台处理器 次要

    优先级1

    11. 程序计数器

    程序计数器是用于存放下一条指令所在单元的地址的地方

    二. 多线程的快速入门

    1. 多线程的应用场景

    1. 客户端(移动App端/)开发;
    2. 异步发送短信/发送邮件
    3. 将执行比较耗时的代码改用多线程异步执行; 可以提高接口的响应速度
    4. 异步写入日志 日志框架底层
    5. 多线程下载

    2. 多线程的创建方式

    1. 继承Thread类创建线程
    2. 实现Runnable接口创建线程
    3. 使用匿名内部类的形式创建线程
    4. 使用lambda表达式创建线程
    5. 使用Callable和Future创建线程
    6. 使用线程池例如用Executor框架
    7. spring @Async异步注解 结合线程池

    2.1 继承Thread类创建线程

    public class ThreadDemo01 extends Thread {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "<run>");
        }
    
        public static void main(String[] args) {
            // 创建一个线程
            ThreadDemo01 threadDemo01 = new ThreadDemo01();
            // 启动线程是start方法而不是run方法
            threadDemo01.start();
        }
    }
    

    2.2 实现Runnable接口创建线程

    public class ThreadDemo02 implements Runnable {
        public void run() {
            System.out.println(Thread.currentThread().getName() + ",我是子线程");
        }
    
        public static void main(String[] args) {
            new Thread(new ThreadDemo02()).start();
        }
    }
    

    2.3 使用匿名内部类的形式创建线程

    //         1.使用匿名内部类创建线程
    new Thread(new Runnable() {
        public void run() {
            System.out.println(Thread.currentThread().getName() + ",>我是子线程<");
        }
    }).start();
    
    

    2.4 使用jdk8的新特性lambda 创建线程

    // 2.lambda表达式创建线程
    new Thread(()->{
        System.out.println(Thread.currentThread().getName() + ",>我是子线程<");
    }).start();
    

    2.5 使用Callable和Future创建线程

    Callable和Future 线程可以获取到返回结果 底层基于LockSupport

    线程 异步执行 比较耗时间-

    从Java 5开始,Java提供了Callable接口,该接口是Runnable接口的增强版,Callable接口提供了一个call()方法,可以看作是线程的执行体,但call()方法比run()方法更强大。

    call()方法可以有返回值。

    call()方法可以声明抛出异常。

    public class ThreadCallable implements Callable<Integer> {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ThreadCallable threadCallable = new ThreadCallable();
            FutureTask<Integer> integerFutureTask = new FutureTask<>(threadCallable);
            new Thread(integerFutureTask).start();
            Integer result = integerFutureTask.get();
            System.out.println(result);
        }
    
        @Override
        public Integer call() throws Exception {
            // 默认代码执行非常耗时!!
            System.out.println(Thread.currentThread().getName() + ",执行计算操作");
            Thread.sleep(3000);
            return 1;
        }
    }
    

    2.6 使用线程池例如用Executors框架

    复用机制

    ExecutorService executorService = Executors.newCachedThreadPool();
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ">我是子线程<");
        }
    });
    

    2.7 spring @Async异步注解

    @Async 底层基于aop+自定义注解实现

    @Async注解对于接口方法或者实现类方法上添加都可以生效,自定义注解实现异步时,接口方法上添加自定义注解不生效,实现类方法上添加自定义注解生效。

    启动类上开启异步注解@EnableAsync

    @SpringBootApplication
    @MapperScan("com.ruangh.aop.*.dao")
    @EnableAsync
    public class DemoStartApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoStartApplication.class,args);
       }
    }
    
    @Component
    @Slf4j
    public class OrderManage {
        @Async
        public void asyncLog() {
            try {
                Thread.sleep(3000);
                log.info("<2>");
            } catch (Exception e) {
    
            }
    
        }
    }
    
    /**
     * http://127.0.0.1:8080/addOrder
     *
     * @return
     */
    @RequestMapping("/addOrder")
    public String addOrder() {
        log.info("<1>");
        orderManage.asyncLog();
        log.info("<3>");
        return "3";
    }
    

    2.8 手写 @Async异步注解

    自定义注解

    @Target(ElementType.METHOD) // 指定新注解可以标注在方法上
    @Retention(RetentionPolicy.RUNTIME) // 指定新注解保留到程序运行时期
    @Inherited // 指定新注解标注在父类上时可被子类继承
    public @interface MyAsync {
        String value() default "";
    }
    

    切面类

    @Aspect
    @Component
    public class LogAspect {
    
        @Pointcut("@annotation(com.ruangh.aop.product.annotation.MyAsync)")
        public void myAsyncPoinCut(){}
    
        @Around("myAsyncPoinCut()")
        public void around2(ProceedingJoinPoint point) throws Throwable {
            new Thread(() -> {
                try {
                    point.proceed();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
            },"myAsync线程").start();
        }
    }
    

    相关文章

      网友评论

          本文标题:多线程基础-7种创建线程方式-手写@Async异步注解

          本文链接:https://www.haomeiwen.com/subject/sornultx.html