首页 > 编程语言 >Java | 一分钟掌握异步编程 | 3 - 线程异步

Java | 一分钟掌握异步编程 | 3 - 线程异步

时间:2023-04-05 10:31:58浏览次数:39  
标签:异步 Java System 任务 线程 println public out

 作者:Mars酱

 声明:本文章由Mars酱编写,部分内容来源于网络,如有疑问请联系本人。

 转载:欢迎转载,转载前先请联系我!

前言

前两篇介绍了理论,这篇讲具体的实现方式。前言都是废话,直接上车~

简单粗暴

创建一个对象,继承Thread类,实现run函数,这个线程异步就做完了:

/**
 * @author mars酱
 */
public class OrderThread extends Thread {
    @Override
    public void run() {
        System.out.println("----------------------------");
        System.out.println("老板: 那小子已经下单了,开工做面!");
        System.out.println("----------------------------");
    }
}

主函数代码:

/**
 * @author mars酱
 */
public class NoodlesRestaurant {
    public static void main(String[] args) {
        System.out.println("你:老板,两碗面条。(潇洒扫一扫,6元巨款两碗面)");
        OrderThread order = new OrderThread();
        order.start();

        System.out.println("你:我俩等下吃碗面,就去看个电影,然后再那个###酒店吧");
        System.out.println("女友:你好坏坏");
    }
}

运行一下:

Java | 一分钟掌握异步编程 | 3 - 线程异步_线程池

线程异步就是这么简单粗暴,干净又卫生~

开门接客

如果来一对小情侣就创建一个对象,事情处理完还要销毁对象,这样反复创建、销毁还是很费体力的,我们遇到这种情况可以使用线程池处理,JDK里面的线程池是java.util.concurrent.ThreadPoolExecutor对象。

我们平常工作中呢一般这样写:

ExecutorService executor = Executors.newFixedThreadPool(3);

ExecutorService executor = Executors.newCachedThreadPool();

ExecutorService executor = Executors.newSingleThreadExecutor();

但是实际上呢,这三种方式最终都是通过java.util.concurrent.ThreadPoolExcutor来完成的,所以我一步到位了。

Executors 中创建三种线程:

newFixedThreadPool: 固定大小的线程池;

newCachedThreadPool: 线程数根据任务动态调整的线程池;

newSingleThreadExecutor: 单线程用的线程池;

看下java.util.concurrent.ThreadPoolExcutor的构造函数吧:

Java | 一分钟掌握异步编程 | 3 - 线程异步_线程池_02

构造函数的参数我给大家翻译翻译,体现一下我英语bia级水平:

corePoolSize: 池中存活的线程数,范围在0 ~ Integer.MAX_VALUE之间,假设现在设置为10,那么新任务来不足10个线程就会创建,否则超过10个线程就会根据情况销毁到只剩10为止;

maximumPoolSize: 最大允许的线程数,

keepAliveTime: 当线程数大于核心数时,keepAliveTime是多余的空闲线程在终止之前等待新任务的最长时间;

unit: 是修饰keepAliveTime的单位,时分秒那种修饰,具体可以看看TimeUnit里面的内容;

workQueue: 队列对象。newSingleThreadExecutor() 和 newFixedThreadPool() 使用的是LinkedBlockingQueue对象队列,newCachedThreadPool()使用的是SynchronousQueue队列,因为newCachedThreadPool()的需求是可以动态调整队列大小的,而SynchronousQueue队列它没有容量,放入一个元素之后只有等它被取出来才能再放入一个;

threadFactory: ThreadFactory对象是线程工厂,工厂就是来创建线程的;

handler: 拒绝策略。当到达线程边界和队列容量而被阻止执行时要使用的处理策略,这里可用的四种策略:

四种拒绝策略:

  1. CallerRunsPolicy:让提交任务的线程自己去执行这个任务,也就意味着这个线程会因此而阻塞;
  2. AbortPolicy:拒绝执行这个任务,并且在提交任务的线程中抛出 java.util.current.RejectedExecutionException异常;
  3. DiscardPolicy:同样拒绝执行这个任务,但是不抛出异常;
  4. DiscardOldestPolicy:从任务队列中把最早加入的任务丢弃,然后把当前任务加进任务队列;

介绍完了,写代码玩玩:

/**
 * @author mars酱
 */
public class NoodlesRestaurant {
    public static void main(String[] args) {
    	// 1. 创建一个线程池,活跃线程5, 最大线程10,等待时长5秒,使用LinkedBlockingQueue队列,拒绝策略使用CallerRunsPolicy
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                10,
                5,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        // 2. 接待一个用户的下单
        threadPoolExecutor.execute(new OrderThread());
    }
}

执行后的结果:

Java | 一分钟掌握异步编程 | 3 - 线程异步_线程池_03

很好,接1个客没问题,那么我们开始大量接客:

改造下单代码,增加桌号,以方便知道拿桌下单了:

public class OrderThread extends Thread {
    Integer deskNum;

    public OrderThread(Integer deskNum) {
        this.deskNum = deskNum;
    }

    @Override
    public void run() {
        System.out.println(">> 老板: " + deskNum + "号桌已下单,开工做面!");
    }

}

餐厅代码也改造一下:

/**
 * @author mars酱
 */
public class NoodlesRestaurant {
    public static void main(String[] args) {
//        System.out.println("你:老板,两碗面条。(潇洒扫一扫,6元巨款两碗面)");
//        OrderThread order = new OrderThread();
//        order.start();
//
//        System.out.println("你:我俩等下吃碗面,就去看个电影,然后再那个###酒店吧");
//        System.out.println("女友:你好坏坏");

        // 1. 创建一个线程池,活跃线程5, 最大线程10,等待时长5秒,使用LinkedBlockingQueue队列,拒绝策略使用CallerRunsPolicy
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,
                10,
                5,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(),
                new ThreadPoolExecutor.CallerRunsPolicy());

        // 2. 模拟10桌客人轮流下单点餐
        for (int i = 0; i < 10; i++) {
            System.out.println(i + "桌 | 潇洒扫一扫~");
            threadPoolExecutor.execute(new OrderThread(i));
            System.out.println(i + "桌 | 点餐完成!");
        }
    }
}

运行结果:

Java | 一分钟掌握异步编程 | 3 - 线程异步_java_04

最后

废话很少,让你拥有充足的一分钟,连代码中互撩的环节也省去了。实际工作中,当应用结束的时候,有两个问题要考虑:

  1. 线程池队列当中未被处理的任务要如何处理?
  2. 正在执行的那些任务要如何处理?

我认为在设计上应该保证,队列中未被处理的任务是可以随时丢弃的。下次启动线程池后,这些未处理的任务可以重新再提交给线程池,业务上不受影响。另外,对于正在执行的任务,要保证在执行任务过程中,一旦进程被结束,那么要有应对的处理策略:

  1. 所有已经作出的状态变更都会回滚,下次执行这个任务又可以再开始。例如:使用数据库事务
  2. 这个任务可以从中间状态继续执行,比如:下载文件中的断点续传。

但不管怎么样,当进程结束时,还是希望应该尽可能等待正在处理的任务执行完成,以减少出错的可能性。ThreadPoolExecutor 提供这样一种方式:

// 告诉线程池不再接受新的任务,也不再处理队列中的任务 
executor.shutdownNow(); 

// 等待线程池中正在执行的任务都处理完毕,最多1小时 
executor.awaitTermination(1, TimeUnit.HOURS);

再最后

多线程异步虽然有缺陷,但是如果你不关心返回结果,而且任务之间相互没有依赖关系,那么多线程异步是个简单粗暴的可靠选择。

标签:异步,Java,System,任务,线程,println,public,out
From: https://blog.51cto.com/u_15424046/6170262

相关文章

  • 走进Java接口测试之Mock(概念篇)
    引言实际工作中,测试人员可能会遇到如下情况:场景一:依赖接口不通,甲开发A模块,乙开发B模块,甲的进度比乙快,但A模块的方法依赖于B模块,要测试A模块接口怎么办?场景二:异常数据难模拟,当需要测试接口一些异常数据,接口正常情况是否无法提供异常数据的。那么如何简便地构造接口的异常数据?场景三:......
  • JavaScript 弹出框(警告框、确认框、提示框)
    JavaScript有三种类型弹出框:警告框、确认框、提示框。一、警告框如果要确保信息传递给用户,通常会使用警告框。当警告框弹出时,用户将需要单击“确定”来继续基础语句 <script> //警告框 window.alert("成功弹出警告框!");//window.alert()方法可以不带window前缀。 ......
  • javaEE进阶小结与回顾(一)
    继承继承的格式:publicclassStudentextendsPeople()父类:子类的共性抽取而来继承的好处,子类可以直接使用父类成员,提高代码复用性与可维护性继承的弊端,使类与类之间产生关联,增强其之间的耦合性,父类变化时子类不得不跟着变化类与类之间具有isa的关系时,适合使用继承,......
  • java学习日记20230406-StringBuilder,StringBuffer,String比较
    StringBuffer,StringBuilder,String比较: StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且方法相同;String:不可变字符序列,效率低,但是复用率高;StringBuffer:可变字符序列,效率较高,线程安全;StringBuider:可变字符序列,效率极高,线程不安全  String使用注意说明: ......
  • java学习日记20230406-StringBuilder类
    StringBuilder类一个可变的字符序列,此类提供一个与StringBuffer兼容的Api,但不保证同步。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候。如果可能,建议优先采用该类,因为在大多数实现中,他比StringBuffer要快----StringBuilder不是线程安全的在S......
  • JavaScript对象
    ArrayString自定义对象 ArrayJavaScriptArray对象用于定义数组定义: var变量名=newArray(元素列表);//方式一var变量名=[元素列表];//方式二 访问arr[索引]=值;arr[0]=1;注意:JS类似于Java集合,长度,类型都可变 Ps.length数组中元素的个数......
  • Java
    Java一、转义字符\t:一个制表符,实现对齐功能\n:换行\\:一个\,其他单引号、双引号同\r:一个回车System.out.print("学习\r北京");ps:回车后,回到行首替换输入二、注释单行注释多行注释(禁止套娃)文档注释javadoc-d生成后的路径-author-version文件名......
  • java学习日记20230405-StringBuffer类
    StringBuffer类java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删很多方法与String相同,但StringBuffer是可变长度的StringBuffer是一个容器StringBuffer是final类实现了Serializable接口,可以保存到文件或网络传输继承了抽象类AbstractStringBuiderAbstra......
  • Java笔记(8) 异常和错误
    异常的简单分类检查性异常:最具代表性的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如用户要打开一个不存在的文件,一个异常就发生了,这些异常在编译时不能被简单的忽略。运行时异常:运行时异常是可能被程序员避免的异常,与检查性异常相反,运行时异常可以在编译时......
  • Java中的 Stream 流02
    1、流的创建1.1、使用集合对象的stream()方法创建流数组对象,创建流时需要使用Arrays.stream()方法;集合类对象,可以在对象后直接使用.stream()方法转换为流;Map对象不能直接转换为流,但是可以对Map对象中的key、value、entrySet分别转换为流方便后续使用。//数组对象in......