Java多线程编程

Wesley13
• 阅读 643

  在计算机中,线程是稀缺资源,创建过多的线程,不仅会消耗系统资源,还会降低系统的稳定性,合理的使用线程池对线程进行统一分配、调优和监控,有以下好处:

  • 降低资源消耗;
  • 提高响应速度;
  • 提高线程的可管理性。

  Java多线程编程常用到多线程框架Executor,使用此框架可以方便、高效的对线程进行管理,我们先了解下Executor。
  Executor是从Java 5开始引入的一个框架,在java.util.concurrent 包下,其内部使用了线程池机制,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好,更方便管理,效率更高。
  Executor框架主要包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future/FutureTask,Runnable/Callable等。

Executor接口

  Executor接口是Executor框架最基本的接口,Executor框架的大部分类都直接或间接地实现了此接口。Executor接口只有一个方法:

void execute(Runnable command);

  该方法接收一个Runable实例参数,用来执行一个任务,这个任务就是一个实现了Runnable接口的类。

ExecutorService接口

  ExecutorService接口继承自Executor接口,它提供了多个方法用于对线程进行管理。

public interface ExecutorService extends Executor {
    //依次关闭任务,在此过程中会执行先前提交的任务,但不接受任何新任务。
    void shutdown();
    
    //阻止等待中的任务启动,并试图停止当前正在执行的任务,不再接收新任务,返回处于等待队列的任务列表
    List<Runnable> shutdownNow();

    //判断线程池是否已经关闭
    boolean isShutdown();

    //如果执行关闭操作后所有任务都已完成,则返回true。注意,除非首先调用 shutdown 或 shutdownNow,否则 isTerminated 永不为 true。
    boolean isTerminated();

    //阻塞并等待被关闭任务执行完成,或发生超时,或发生中断  
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

    //提交一个有返回值的 Callable 任务用于执行,并返回一个表示任务的未决结果的Future。任务成功执行时,该Future的get()方法将会返回执行结果。
    <T> Future<T> submit(Callable<T> task);

    //提交一个 Runnable 任务用于执行,并返回一个表示该任务的Future。任务成功执行时,该Future的get()方法将会返回给定的结果 result。
    <T> Future<T> submit(Runnable task, T result);

    //提交一个 Runnable 任务用于执行,并返回一个表示该任务的Future。任务成功执行时,该Future的get()方法将会返回null   
    Future<?> submit(Runnable task);

    //执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;

    //执行给定的任务,当所有任务完成时,返回保持任务状态和结果的 Future 列表。
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
        throws InterruptedException;

    //执行给定的任务,给定的任务中任何一个任务完成时,则返回其结果。一旦正常或异常返回,则取消尚未完成的任务。
    <T> T invokeAny(Collection<? extends Callable<T>> tasks)
        throws InterruptedException, ExecutionException;

    //执行给定的任务,给定的超时时间内任何一个任务完成时,则返回其结果。一旦正常或异常返回,则取消尚未完成的任务。
    <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

Executors类

  Executors是Java线程池的工厂类,它提供了一系列方法用于创建线程池,返回的线程池都实现了ExecutorService接口。 

  常用的创建线程池的方法:

public static ExecutorService newSingleThreadExecutor();

  创建一个单线程的线程池。这个线程池用唯一的工作线程来执行任务,所有任务按照指定顺序依次执行。

public static ExecutorService newFixedThreadPool(int nThreads);

  创建具有固定的线程数量的线程池。线程池中的这些线程可重用,当所有线程都处于活动状态时,再次提交的任务都会加入等待队列,等待其他线程结束,当有线程处于空闲状态时会被下一个任务复用。

public static ExecutorService newCachedThreadPool();

  创建一个可缓存线程的线程池,调用execute()将重用以前构造的线程(如果有线程可用)。如果当前没有线程可用,则创建一个新线程并添加到线程池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
可以看出缓存线程池大小是不定值,根据需要会创建不同数量的线程。在使用缓存型线程池时,先查看线程池中有没有空闲线程,如果有,就复用;如果没有,新建线程并加入线程池中。缓存型线程池通常用于执行一些生存期很短的异步型任务。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);

  创建一个支持定时及周期性任务执行的线程池。此线程池中的线程可延迟执行,定时执行或固定周期执行。

CompletionService

  CompletionService接口用于异步获取并行任务的执行结果。使用Future和Callable可以获取线程的执行结果,但是获取的方式是阻塞的,需要根据线程添加到线程池中的顺序依次获取,如果获取不到就会阻塞。为了解决这种情况,可以使用CompletionService非阻塞的获取任务的执行结果。CompletionService异步非阻塞的获取任务执行结果是通过队列来实现的,一个任务执行完成后就将其执行结果放到队列中,获取结果是从队列中取,先执行结束的线程的结果先被获取,避免了某个线程阻塞会影响后续线程执行结果获取的情况。

Runnable/Callable

  实现了Runnable或Callable接口的类就是线程的执行类,线程业务代码要被封装在实现了Runnable或Callable接口的类中。实现了Runnable接口的线程业务类可以使用execute()方法直接执行,也可以使用ExecutorService的submit()方法提交并执行。实现了Callable接口的线程业务类,只能通过ExecutorService的submit(Callable task) 方法来提交并执行,并且返回一个Future,这个Future表示任务的未决执行结果。

线程池

  表示线程池的类有两个:ThreadPoolExecutor 和 ScheduledThreadPoolExecutor,Executors类创建的线程池就属于这两个类中的一个。

  ThreadPoolExecutor:单线程线程池、可缓存的线程池、固定数目线程的线程池都是ThreadPoolExecutor类。
  ScheduledThreadPoolExecutor: 支持定时及周期性的任务的线程池是ScheduledThreadPoolExecutor类。

示例

   待补充

参考:

  https://blog.csdn.net/fangqun663775/article/details/78961417
  https://www.cnblogs.com/fengsehng/p/6048610.html
  https://blog.csdn.net/tongdanping/article/details/79604637

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
java各种面试问题
二、Java多线程相关线程池的原理,为什么要创建线程池?创建线程池的方式;线程的生命周期,什么时候会出现僵死进程;说说线程安全问题,什么实现线程安全,如何实现线程安全;创建线程池有哪几个核心参数?如何合理配置线程池的大小?volatile、ThreadLocal的使用场景和原理;
Wesley13 Wesley13
3年前
4、jstack查看线程栈信息
1、介绍利用jps、top、jstack命令找到进程中耗时最大的线程,以及线程状态等等,同时最后还可以显示出死锁的线程查找:FoundoneJavaleveldeadlock即可1、jps获得进程号!(https://oscimg.oschina.net/oscnet/da00a309fa6
Wesley13 Wesley13
3年前
Java编程思想笔记整理
实现线程的方法:(1)继承thread(底层实现了runable)(2)实现Runable(3)Executor创建线程池(4)实现Callable接口(带返回结果)对于callable接口,可以通过FutureTask包装实现线程,也可以使用ExecutorService对象的submit实现。使用executor创建线程
Stella981 Stella981
3年前
Executor框架
任务是一组逻辑工作单元,而线程则是使任务异步执行的机制。线程池简化了线程的管理工作,并且java.util.concurrent提供了一种灵活的线程池实现作为Executor框架的一部分。在Java类库中,任务执行的主要抽象不是Thread,而是Executor,如下所示:publicinterfaceExecutor{void
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Stella981 Stella981
3年前
Noark入门之线程模型
0x00单线程多进程单线程与单进程多线程的目的都是想尽可能的利用CPU,减少CPU的空闲时间,特别是多核环境,今天咱不做深度解读,跳过...0x01线程池锁最早的一部分游戏服务器是采用线程池的方式来处理玩家的业务请求,以达最大限度的利用多核优势来提高处理业务能力。但线程池同时也带来了并发问题,为了解决同一玩家多个业务请求不被
Wesley13 Wesley13
3年前
2万字Java并发编程面试题整理(含答案,建议收藏)
Java并发编程1、在java中守护线程和本地线程区别?2、线程与进程的区别?3、什么是多线程中的上下文切换?4、死锁与活锁的区别,死锁与饥饿的区别?5、Java中用到的线程调度算法是什么?6、什么是线程组,为什么在Java中不推荐使用?7、为什么使用Executor框架?8、在Java
Wesley13 Wesley13
3年前
Java中的线程池
java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理使用线程池能够带来三个好处。第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺