JAVA并发学习

news2024/12/23 22:31:12

1 基础准备

1.1 并发与并行

并发是不同的代码块交替执行,也就是交替可以做不同的事情。

并行是不同的代码块同时执行,也就是同时可以做不同的事情。

根据CPU 核数,线程运行是不同的

单核CPU(微观串行,宏观并行)操作系统的任务调度器,将 cpu 的时间片(windows 下时间片最小约为 15 毫秒)分给不同的线程使用,由于时间片切换很快,人的感觉是在并行,实际还是串行执行的。

 多核 cpu下,每个 核(core)都可以调度运行线程,这时候线程可以是并行的。

 1.2 进程、线程

(1)进程

进程基本上是一个正在执行的程序,它是操作系统中最小的资源分配单位。

进程的上下文切换是指 cpu 从一个进程切换到另一个进程。

进程上下文切换主要包含两个主要过程:进程地址空间切换和处理器状态切换。

(2)线程

线程是进程的子集,也称为轻量级进程。一个进程可以有多个线程,这些线程由调度器独立管理。一个进程内的所有线程都是相互关联的。线程是操作系统中最小的调度单位。每个进程至少包含一个线程,每个进程的初始线程被称作主线程。

线程没有自己的地址空间,同一进程的线程之间切换,他们共享同一进程的地址空间,所以只需要切换处理器状态;不同进程的线程之间切换,会引起进程切换。

2 并发实现

2.1 原子操作

用过synchronized 关键字来保证一次只有一个线程在执行代码块。

public synchronized void code() {
    // TODO
}

Volatile 关键字保证任何线程在读取Volatile修饰的变量的时候,读取的都是这个变量的最新数据。

使用lock锁机制,其中也包括相应的读写锁

2.2 Thread

public class MyRunnable implements Runnable {
    @Override
    public void run() {
     // TODO
    }
}

public class Main {
    public static void main(String[] args) {
        Runnable task = new MyRunnable();
        Thread worker = new Thread(task);
        worker.setName('Myrunnable');
        worker.start();
}

new Thread的弊端如下:

        a. 每次new Thread新建对象性能差。
        b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
        c. 缺乏更多功能,如定时执行、定期执行、线程中断。 

2.3 Executor

Java通过Executors提供四种线程池,分别为:

  • newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
    public class MyRunnable implements Runnable {
        @Override
        public void run() {
         // TODO
        }
    }    

    private static final int NUM = 5;
    public static void main(String[] args) {
        ExecutorService pool = Executors.newSingleThreadExecutor();
        MyRunnable myRunnable = new MyRunnable();
        pool.submit(myRunnable);
       
        if(pool.isShutdown()){
            //  ....
        }
    }

2.4 Future

Runnable只是一个接口,里面只是声明了一个run方法,可以看到方法的返回值是void,所以线程执行完了没有任何的返回值;Callable也是一个接口,它里面声明了一个call方法,可以看到它是一个泛型接口,call()函数返回的类型就是传递进来的V类型。

public interface Callable<V> {
    V call() throws Exception;
}

线程的执行是异步的,一个线程和另外一个线程的执行是互不干扰的,Futrue可以监视目标线程调用call的情况,当你调用Future的get()方法以获得结果时,当前线程就开始阻塞,直接call方法结束返回结果。

所以通过实现Callback接口,并用Future可以来接收多线程的执行结果。

主要方法:

  • get()方法可以当任务结束后返回一个结果,如果调用时,工作还没有结束,则会阻塞线程,直到任务执行完毕
  • get(long timeout,TimeUnit unit)获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null,这个就避免了一直获取不到结果使得当前线程一直阻塞的情况发生
  • cancel(boolean mayInterruptIfRunning)用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。
    • 参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。
    • 如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false;
    • 如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;
    • 如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true
  • isDone()方法判断当前方法是否完成
  • isCancel()表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true

demo案例:

#假设存在两个任务
public class UserInfoService {

    public UserInfo getUserInfo(Long userId) throws InterruptedException {
        Thread.sleep(300);//模拟调用耗时
        return new UserInfo("666", "捡田螺的小男孩", 27); //一般是查数据库,或者远程调用返回的
    }
}

public class MedalService {

    public MedalInfo getMedalInfo(long userId) throws InterruptedException {
        Thread.sleep(500); //模拟调用耗时
        return new MedalInfo("666", "守护勋章");
    }
}


#实现异步调用
public class FutureTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ExecutorService executorService = Executors.newFixedThreadPool(10);

        UserInfoService userInfoService = new UserInfoService();
        MedalService medalService = new MedalService();
        long userId =666L;
        long startTime = System.currentTimeMillis();

        //调用用户服务获取用户基本信息
        FutureTask<UserInfo> userInfoFutureTask = new FutureTask<>(new Callable<UserInfo>() {
            @Override
            public UserInfo call() throws Exception {
                return userInfoService.getUserInfo(userId);
            }
        });
        executorService.submit(userInfoFutureTask);

        Thread.sleep(300); //模拟主线程其它操作耗时

        FutureTask<MedalInfo> medalInfoFutureTask = new FutureTask<>(new Callable<MedalInfo>() {
            @Override
            public MedalInfo call() throws Exception {
                return medalService.getMedalInfo(userId);
            }
        });
        executorService.submit(medalInfoFutureTask);

        UserInfo userInfo = userInfoFutureTask.get();//获取个人信息结果
        MedalInfo medalInfo = medalInfoFutureTask.get();//获取勋章信息结果

        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");
    }
}

3 CompletableFuture

3.1 CompletableFuture使用

CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。

public class FutureTest {

    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {

        UserInfoService userInfoService = new UserInfoService();
        MedalService medalService = new MedalService();
        long userId =666L;
        long startTime = System.currentTimeMillis();

        //调用用户服务获取用户基本信息
        CompletableFuture<UserInfo> completableUserInfoFuture = CompletableFuture.supplyAsync(() -> userInfoService.getUserInfo(userId));

        Thread.sleep(300); //模拟主线程其它操作耗时

        CompletableFuture<MedalInfo> completableMedalInfoFuture = CompletableFuture.supplyAsync(() -> medalService.getMedalInfo(userId)); 

        UserInfo userInfo = completableUserInfoFuture.get(2,TimeUnit.SECONDS);//获取个人信息结果
        MedalInfo medalInfo = completableMedalInfoFuture.get();//获取勋章信息结果
        System.out.println("总共用时" + (System.currentTimeMillis() - startTime) + "ms");

    }

CompletableFuture提供了几十种方法,辅助我们的异步任务场景。这些方法包括创建异步任务、任务异步回调、多个任务组合处理等方面。

CompletableFuture创建异步任务,一般有supplyAsync和runAsync两个方法

  • supplyAsync执行CompletableFuture任务,支持返回值
  • runAsync执行CompletableFuture任务,没有返回值。

//使用默认内置线程池ForkJoinPool.commonPool(),根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
//自定义线程,根据supplier构建执行任务
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)

//使用默认内置线程池ForkJoinPool.commonPool(),根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable) 
//自定义线程,根据runnable构建执行任务
public static CompletableFuture<Void> runAsync(Runnable runnable,  Executor executor)

3.2 异步回调

1)thenRun/thenRunAsync

通俗点讲就是,做完第一个任务后,再做第二个任务。某个任务执行完成后,执行回调方法;但是前后两个任务没有参数传递,第二个任务也没有返回值 

     
        CompletableFuture<Void> cp1 = CompletableFuture.runAsync(() -> {
            try {
                //执行任务A
                Thread.sleep(600);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

        CompletableFuture<Void> cp2 =  cp1.thenRun(() -> {
            try {
                //执行任务B
                Thread.sleep(400);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

2)thenAccept/thenAcceptAsync
第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,但是回调方法是没有返回值的。

      CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {
            return "dev";

        });
      CompletableFuture<Void> cp2 =  cp1.thenAccept((a) -> {
            System.out.println("上一个任务的返回结果为: " + a);
        });
     
      cp2.get();

3)thenApply/thenApplyAsync
表示第一个任务执行完成后,执行第二个回调方法任务,会将该任务的执行结果,作为入参,传递到回调方法中,并且回调方法是有返回值的。

        CompletableFuture<String> cp1 = CompletableFuture.supplyAsync(() -> {
            return "dev";

        }).thenApply((a) -> {
            if(Objects.equals(a,"dev")){
                return "dev";
            }
            return "prod";
        });

3.3 多任务组合回调

thenCombine / thenAcceptBoth / runAfterBoth都表示:「当任务一和任务二都完成再执行任务三」

区别在于:

  • 「runAfterBoth」 不会把执行结果当做方法入参,且没有返回值
  • 「thenAcceptBoth」: 会将两个任务的执行结果作为方法入参,传递到指定方法中,且无返回值
  • 「thenCombine」:会将两个任务的执行结果作为方法入参,传递到指定方法中,且有返回值
   @Test
    public void testCompletableThenCombine() throws ExecutionException, InterruptedException {
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //开启异步任务1
        CompletableFuture<Integer> task = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步任务1,当前线程是:" + Thread.currentThread().getId());
            int result = 1 + 1;
            System.out.println("异步任务1结束");
            return result;
        }, executorService);

        //开启异步任务2
        CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("异步任务2,当前线程是:" + Thread.currentThread().getId());
            int result = 1 + 1;
            System.out.println("异步任务2结束");
            return result;
        }, executorService);

        //任务组合
        CompletableFuture<Integer> task3 = task.thenCombineAsync(task2, (f1, f2) -> {
            System.out.println("执行任务3,当前线程是:" + Thread.currentThread().getId());
            System.out.println("任务1返回值:" + f1);
            System.out.println("任务2返回值:" + f2);
            return f1 + f2;
        }, executorService);

        Integer res = task3.get();
        System.out.println("最终结果:" + res);
    }

applyToEither / acceptEither / runAfterEither 都表示:「两个任务,只要有一个任务完成,就执行任务三」

区别在于:

  • 「runAfterEither」:不会把执行结果当做方法入参,且没有返回值
  • 「acceptEither」: 会将已经执行完成的任务,作为方法入参,传递到指定方法中,且无返回值
  • 「applyToEither」:会将已经执行完成的任务,作为方法入参,传递到指定方法中,且有返回值

3.4 其他

Future需要获取返回值,才能获取异常信息

  @Test
    public void test() {
        CompletableFuture<Double> future = CompletableFuture.supplyAsync(() -> {
            if (1 == 1) {
                throw new RuntimeException("出错了");
            }
            return 0.11;
        });

        //如果不加 get()方法这一行,看不到异常信息
        //future.get();
    }

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

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

相关文章

YOLOV5 + PYQT5双目测距(一)

YOLOV5 PYQT5双目测距 1. 测距源码2. 测距原理3. PYQT环境配置4. 实验结果4.1 界面1&#xff08;简洁版&#xff09;4.2 界面2&#xff08;改进版&#xff09; 1. 测距源码 详见文章 YOLOV5 双目测距&#xff08;python&#xff09; 2. 测距原理 如果想了解双目测距原理&a…

Stable Diffusion 指定模型人物,Lora 训练全流程

简介 在使用 Stable Diffusion 的时候&#xff0c;可以选择别人训练好的 Lora&#xff0c;那么如何训练自己的 Lora&#xff0c;本篇文章介绍了介绍了如何训练Lora&#xff0c;如何从训练的模型中选择好的模型&#xff0c;如何在 Stable Diffusion 中使用。 闲话不多说&#…

CUDA编程接口详解

CUDA编程接口详解 本文将详细介绍NVIDIA CUDA编程指南第3章&#xff08;编程接口&#xff09;中的核心概念&#xff0c;例如NVCC编译器、CUDA运行时、版本管理和兼容性、计算模式、模式切换以及Windows下的Tesla计算集群模式。以下是本文的大纲&#xff1a; 文章目录 CUDA编程…

《斯坦福数据挖掘教程·第三版》读书笔记(英文版)Chapter 11 Dimensionality Reduction

来源&#xff1a;《斯坦福数据挖掘教程第三版》对应的公开英文书和PPT Chapter 11 Dimensionality Reduction Let M be a square matrix. Let λ be a constant and e a nonzero column vector with the same number of rows as M. Then λ is an eigenvalue of M and e is t…

快手三面全过了,却因为背调时leader手机号造假,导致offer作废了!

这是一个悲伤的故事&#xff1a; 快手本地三面全过了&#xff0c;但因为背调时leader手机号造假&#xff0c;导致offer作废了。 楼主感叹&#xff1a;大家背调填写信息时&#xff0c;一定要慎重再慎重&#xff0c;不要重复他的悲剧&#xff01; 网友愤慨&#xff0c;照这么说&a…

【Nginx 优化与防盗链】

目录 一、Nginx 服务优化1、配置Nginx 隐藏版本号2、修改用户与组3、缓存时间4、日志切割小知识 二、Nginx 深入优化1、连接超时2、更改进程数3、配置网页压缩4、配置防盗链 一、Nginx 服务优化 1、配置Nginx 隐藏版本号 可以使用 Fiddler 工具抓取数据包&#xff0c;查看 Ng…

UniApp全局弹窗

一、设计思路 1、创建一个弹窗页面组件 2、配置page.json&#xff0c;使页面跳转是在当前界面展示 3、定义uni全局全局属性 4、解决多个弹窗同时使用的冲突问题 注意&#xff1a;此方案不支持多个弹窗并存&#xff0c;有且仅有一个会展示&#xff0c;当前弹窗展示并关闭上一个弹…

连锁药店系统:如何提高效率和客户满意度?

连锁药店系统是一种用于提高效率和客户满意度的软件系统&#xff0c;它能够管理多个药店的日常营运。通过这种系统&#xff0c;药店可以更好地管理库存、员工、销售和客户信息等方面&#xff0c;从而提高整体的经营效率。 首先&#xff0c;连锁药店系统能够帮助药店管理库存。系…

黑马Redis视频教程实战篇(六)

目录 一、附近商户 1.1、GEO数据结构的基本用法 1.2、导入店铺数据到GEO 1.3、实现附近商户功能 二、用户签到 2.1、BitMap功能演示 2.2、实现签到功能 2.3、签到统计 2.4、关于使用bitmap来解决缓存穿透的方案 三、UV统计 3.1、HyperLogLog 3.2、测试百万数据的统…

I.MX6ull 中断 二 (按键驱动蜂鸣器)

按键中断 KEY0 &#xff08;UART1_CTS 引脚&#xff09;触发蜂鸣器 1 修改start.S 添加中断相关定义 中断向量表 .global _start /* 全局标号 *//** 描述&#xff1a; _start函数&#xff0c;首先是中断向量表的创建* 参考文档:ARM Cortex-A(armV7)编程手册V4.0.pdf P…

【花雕学AI】ChatGPT帮我快速优化标题:古老的非洲部落,有一种神奇的超音速烫脚舞

关于非洲烫脚舞&#xff0c;直接看看ChatGPT的许多创意&#xff0c;一般人确实想不到&#xff1a; 部落文化的声动震波 非洲之歌:部落的音速节奏 非洲土著的音速脚掌传奇 古老部落的震人心魂之舞 非洲红土之声:脚掌舞的激情 非洲神秘部落的超音速脚掌舞 仙踪般的部落音乐…

chatgpt赋能python:Python绘制函数曲线:创造出令人惊叹的图形

Python绘制函数曲线&#xff1a;创造出令人惊叹的图形 随着越来越多的人开始关注数据可视化&#xff0c;Python成为了一种被广泛使用的工具&#xff0c;用于创建各种图形&#xff0c;包括函数曲线。Python图形库的灵活性和适用性使得它成为数据科学和工程领域中最受欢迎的编程…

如何用 ChatGPT 一句话生成 Web 应用?

原型系统的开发对很多不会编程的人来说&#xff0c;原本确实是一道门槛&#xff0c;而且看似难以逾越。而现在&#xff0c;障碍突然间就消失了。 插件 ChatGPT 现在有了一个内容比较丰富的插件系统&#xff0c;而且 Plus 用户已经不再需要填表申请后漫长等待&#xff0c;直接就…

英雄算法联盟 | 六月算法集训顺利开始

文章目录 前言一、集训规划二、星友的反馈1、有觉得题目简单重新找回了自信的2、有拿到题不管三七二十一疯狂输出的3、有为了完成当天作业奋斗到凌晨的4、有自己悟出了坚持就是胜利的道理的5、有发现身边人都在跑而跃跃欲试的6、有上班摸鱼刷题只因为了赶进度的7、有看到大家都…

【微信小程序开发】第 2 节 - 注册小程序开发账号

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、缘起 2、注册小程序开发账号 3、总结 1、缘起 开发微信小程序从大的方面来说主要分为三步&#xff1a; ① 注册小程序开发…

【观察】星环科技:布局行业大模型赛道,加速国产化替代进程

以ChatGPT和GPT所代表的大模型&#xff0c;已经在国内形成了“海啸效应”&#xff0c;几乎所有的科技公司都在想方设法进入大模型的赛道。背后的核心驱动力&#xff0c;就在于大模型的最大价值在于普遍提升个人生产力&#xff0c;而各行各业的公司都在积极寻找应用大模型和生成…

黑客使用哪些编程语言

黑客使用哪些编程语言&#xff1f; 使用 Python 分析漏洞利用数据库 克里斯蒂安科赫 迈向数据科学 2021 年&#xff0c;我们与科学家同行一起在德国混沌计算机俱乐部 &#xff08;CCC&#xff09; 进行了一项调查。我们的目标是找出黑客最常使用的编程语言。 本文跟进调查&…

M F C(七)对话框

概念 与用户进行交互的窗口&#xff0c;它的顶级父类为CWND&#xff0c;对话框上面可以有各种控件&#xff0c;控件也是继承自CWND 基本控件功能对应的类静态文本框显示文本&#xff0c;一般不能接收输入信息CStatic图像控件显示图标、方框、和图元文件CStatic编辑器编辑正文…

公网SSH远程连接Termux – 电脑使用安卓Termux 「无需公网IP」

文章目录 1.安装ssh2.安装cpolar内网穿透3.远程ssh连接配置4.公网远程连接5.固定远程连接地址 转载自cpolar极点云的文章&#xff1a;公网SSH远程连接Termux – 电脑使用安卓Termux 「无需公网IP」 使用安卓机跑东西的时候&#xff0c;屏幕太小&#xff0c;有时候操作不习惯。不…

【Linux】crontab 定时任务

当你需要在Linux系统中定期执行某些任务时&#xff0c;crontab&#xff08;cron table&#xff09;是一个非常有用的工具。它允许你根据预定的时间表创建和管理定时任务。 一、从守护进程到crond进程1.1 Linux 守护进程1.2 任务调度进程crond 二、 crontab 详细介绍2.1 crontab…