专栏首页一猿小讲Java程序跑的快,全要靠线程带

Java程序跑的快,全要靠线程带

作为 Java 程序员,在技术面试时,多线程的知识多少都会被提及,这也是我面试候选人时,常聊的一个话题。

纳尼,面试中为什么常会问多线程的知识?难道面试官真的是在为难你吗?

莫急,下面一起 get 其中之奥秘。

1

使用场景:引入多线程,明确职责,效率明显提升

在实际项目开发中,经常会遇到订单接收处理、发送通知等场景,研发人员经常会借助多线程的方式,来提高程序的处理性能。

例如:付款业务订单处理的场景。

如上图示意,业务处理流程很简单,通过多线程的方式接收业务订单,然后落入缓冲队列 MQ,最后通过多个消费线程进行付款订单的业务处理。

很显然,在采用生产者消费者模型,进行业务处理时,能够解耦订单接收与订单处理的线程,在一定程度上能够缓解性能瓶颈对系统性能带来的影响。

例如:系统监控报警通知场景。

private final static ExecutorService EXEC = Executors.newFixedThreadPool(5); 

public static void asynSend(final String email, final String title, final String content){
    EXEC.execute(new Runnable(){
      @Override
      public void run() {
        try {
          sendTextEmail(email, title, content, null);
          LOGGER.info("发送邮件{}成功!",email);
        } catch (Exception e) {
          LOGGER.error("发送邮件{},失败原因是{}",email, e);
        }
      }
    });
  }

随意摘的一段通过 Email 方式进行通知用户的代码,能够清楚的看到,发送的任务交给线程池去处理,进而提高程序的性能。

仅以上面两个业务场景,简单描述多线程的使用场景。而回归到生活本质,单线程程序如同只雇佣一个服务员的餐厅,他必须做完一件事情后才可以做下一件事情;而多线程程序则如同雇佣多个服务员的餐厅,各司其职,分工明确,可以同时进行多件事情,效率肯定会提高。

而在项目研发时,为什么多数同学未曾亲自开发过线程相关的代码模块呢?原因是在 Java 的世界里,很多复杂的东西都已被大佬们封装在框架或者组件里啦(eg:Tomcat、Canal、Spring 全家桶等),所以你只管利用好框架或组件,好好开发业务就好了。

2

创建方式:“茴”字有四种写法,线程有几种创建方式呢?

在 Java 的世界里,大家最熟悉的线程的创建方式,莫过于 Java 提供的 Thread 类和 Runnable 接口。

方式一:继承 Thread 类创建线程。

// 1. 定义 Thread 类的子类
class NotifyThread extends Thread {
    // 2. 重写 Thread 类的 run 方法,该方法代表了线程需要完成的任务。
    @Override
    public void run() {
        System.out.println("报警通知");
    }
}

/**
 * 线程创建的方式,so easy!
 * @author 一猿小讲
 */
public class NotifyService {
    public static void main(String[] args) {
        //3. 创建线程对象,并调用线程对象的 start 方法启动线程
        new NotifyThread().start();
    }
}

方式二:实现 Runnable 接口创建线程。

// 1. 定义 Runnable 接口的实现类
class NotifyThread implements Runnable {
    // 2. 实现该接口的 run 方法,该方法是线程的执行体。
    public void run() {
        System.out.println("报警通知");
    }
}

/**
 * 线程创建的方式,so easy!
 * @author 一猿小讲
 */
public class NotifyService {
    public static void main(String[] args) {
        //3. 创建 Runnable 实现类的实例
        NotifyThread notifyThread = new NotifyThread();
        //4. 将创建的实例作为 Thread 的 target 来创建 Thread 对象
        new Thread(notifyThread).start();
    }
}

面试考点:方式一 vs 方式二。

很多同学脑袋中可能就只有上面两种创建线程的方式,面试时多数同学答到这儿就结束啦。

不过,从 JDK1.5 开始,Java 提供了 Callable 接口,提供另一种创建线程的方式。

如上面 Callable 接口源码所示,提供了一个 call 方法,可以作为线程执行体,类比着 Runnable 去看,有点像 Runnable 接口的增强版,比 Runnable 的 run 方法功能感觉更强大,只因 call 方法有返回值,并可以声明抛出异常。

如上面 Thread 源码所示,Thread 的 target 对象接收的是 Runnable,而 Callable 接口并非 Runnable 的子接口,所以 Callable 对象无法直接作为 Thread 的 target ,那该怎么办呢?另外 call 方法作为线程执行体,那方法的返回值该如何获取呢?

这不,JDK1.5 提供了 Future 接口来代表 Callable 接口里 call 方法的返回值,并且为 Future 接口提供了一个实现类 FutrueTask。

采用 Callable 和 Future 创建线程的示例代码,仔细去看很简单。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
 * 线程创建的方式,so easy!
 * @author 一猿小讲
 */
public class NotifyService {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1. 创建 Callable 对象
        NotifyThread thread = new NotifyThread();
        // 2. 使用 FutureTask 来包装 Callable 对象
        FutureTask<Integer> task = new FutureTask<Integer>(thread);
        // 3. 实质还是以 Callable 对象来创建、并启动线程
        new Thread(task, "报警通知线程").start();
        // 4. 获取线程执行结果
        Integer notifyRes = task.get();
        System.out.println("通知结果:" + notifyRes);
    }
}

// 1. 创建 Callable 接口的实现类,并实现 call 方法
class NotifyThread implements Callable<Integer> {
    // 2. 实现 call 方法,该方法将作为线程执行体
    public Integer call() {
        System.out.println("报警通知");
        //3. call 方法可以有返回值
        return 8866;
    }
}

3

寄语写最后

本次,主要对技术面试时常被谈及的多线程知识,进行初步的讲解,后续会逐步进行深入。不过,若想要快速投入实战,还要靠多写、多悟,熟能生巧罢了。

好了,本次就谈到这里,一起聊技术、谈业务、喷架构,少走弯路,不踩大坑。会持续输出原创精彩分享,敬请期待!

本文分享自微信公众号 - 一猿小讲(yiyuanxiaojiangV5),作者:一猿小讲

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 动作要快,姿势要帅!新年「薅羊毛」全靠这个小程序

    眼看着春节越来越近了,人们都一脸欢欣地准备着年货、车票,在辛勤地工作一年之后,他们终于要迎来团聚放松的节日,迎接新一年的到来。

    知晓君
  • K哥打算写Netty了,有深度的那种~

    网络应用程序框架,也就是说明它是用来进行网络传输的,也就是说我们会接触到一些网络层的东西。

    Python进击者
  • Java 程序该怎么优化?实战篇

    另外,为了方便收藏,文末把 Java 程序优化及问题排查套路,整理成了葵花宝典,一定要记得收藏呦。

    一猿小讲
  • 阿里《JAVA实习生入职测试题—2019最新》之答案详解(连载二)

    3、反射中,Class.forName和ClassLoader.loadClass的区别

    NaughtyCat
  • 一个白学家眼里的 WebAssembly

    作者:doodlewind | 花名雪碧 | github.com/doodlewind

    ConardLi
  • 写代码全靠复制粘贴的程序员,可能要失业了

    王新民 | 编译自TechCrunch 量子位·QbitAI 出品 在码农界,有一个古老的传说,那些伟大的程序员们,大部分代码都是从StackOverflow问...

    量子位
  • 怎么样学习Java才能达到自己最想要的效果

      如今最火的IT技术当属Java软件开发了,很多同学都想学习这门语言,那么对于这些想学Java编程的同学只有两种学习方式:自学Java或者参加Java培训班,...

    动力节点Java培训
  • 我花一个月时间整理了新人转行IT最爱问都问题

    我不能一一全部解答,我收集了比较有代表性都问题,如果你也是准备转行程序员的人,相信你也会纠结同样的问题。

    王炸
  • 线程池大小 + 线程数量到底设置多少?

    抛开一些操作系统,计算机原理不谈,说一个基本的理论(不用纠结是否严谨,只为好理解):一个CPU核心,单位时间内只能执行一个线程的指令 ** 那么理论上,我一个线...

    Java小咖秀
  • java面试线程必备知识点,怼死面试官,从我做起

    内存屏障:限制命令操作顺序,有LoadLoad、LoadStore、LoadStore、StroreStreo四种屏障

    用户1257393
  • 序列化系列(1)——JDK序列化和Hessian序列化

    这么说太抽象了,举一个例子:你如果想让一个女孩子知道你喜欢她,你可以给她写情书,这样 「喜欢」 这种状态信息就变成了 「文字」 这种可以存储或传输的信息。

    出其东门
  • 惊呆了,Spring Boot居然这么耗内存!

    Spring Boot总体来说,搭建还是比较容易的,特别是Spring Cloud全家桶,简称亲民微服务,但在发展趋势中,容器化技术已经成熟,面对巨耗内存的Sp...

    Java技术江湖
  • 请给SpringBoot多一些内存

    SprintBoot总体来说,搭建还是比较容易的,特别是SpringCloud全家桶,简称亲民微服务,但在发展趋势中,容器化技术已经成熟,面对巨耗内存的Spri...

    JAVA葵花宝典
  • 万万没想到,Spring Boot 竟然这么耗内存!

    Spring Boot总体来说,搭建还是比较容易的,特别是Spring Cloud全家桶,简称亲民微服务。

    Java技术栈
  • 写一写我从工地转行互联网it的辛酸历程

    成功的从工地转行到办公室办公也有一年了,楼主就来说说,从工地到白领的过渡吧,这其中历经艰辛,最终终达成目标。没错,楼主现在成为了一位java攻城狮....加班狗...

    JAVA葵花宝典
  • 杂谈 什么是伪共享(false sharing)?

    主内存(RAM)是数据存放的地方,CPU 和主内存之间有好几级缓存,因为即使直接访问主内存也是非常慢的。

    彤哥
  • 广东人,请收藏这个超级方便的小程序

    腾讯大讲堂
  • 创业公司中的Java高效应用

    摘要 Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特...

    IT大咖说
  • java面试线程必备知识点,怼死面试官,从我做起

    内存屏障:限制命令操作顺序,有LoadLoad、LoadStore、LoadStore、StroreStreo四种屏障

    用户1257393

扫码关注云+社区

领取腾讯云代金券