爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      jvm专题(4) - 【2/3】多线程-实现

      首页 知识中心 软件开发 文章详情页

      jvm专题(4) - 【2/3】多线程-实现

      2024-09-24 06:30:20 阅读次数:142

      jvm,多线程

      本章内容比较简单,主要是对Thread的实现方式做下初步介绍,稍带着描述下相关方法的原理和使用场景。此节的内容也是开发同学能不能写出高质量线程程序的一个基础,同样也非常重要。

      一、基础知识

      还是按之前文档描述的习惯,用一张图来概念下本章的内容,如下图所示红框内描述所示:(理论基础请看上一章内容,线程协同主要是讲锁相关的知识的后续会做为第三节补充上):

      jvm专题(4) - 【2/3】多线程-实现

      二、Thread基本方法

      线程的基本方法基本就是Object和Thread这两个API提供的,它们控制着线程的生命周期和状态的流转,如下图所示:

      jvm专题(4) - 【2/3】多线程-实现

      2.1、Object基本方法

      2.1.1、线程等待(wait)

      调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断才会返回,需要注意的是调用 wait()方法后,会释放对象的锁。因此,wait 方法一般用在同步方法或同步代码块中。

      2.1.2、线程让步(yield)

      yield 会使当前线程让出 CPU 执行时间片,与其他线程一起重新竞争 CPU 时间片。一般情况下,优先级高的线程有更大的可能性成功竞争得到 CPU 时间片,但这又不是绝对的,有的操作系统对线程优先级并不敏感。

      2.1.3、等待其他线程终止(join )

      join() 方法,等待其他线程终止,在当前线程中调用一个线程的 join() 方法,则当前线程转为阻塞状态,回到另一个线程结束,当前线程再由阻塞状态变为就绪状态,等待 cpu 的宠幸。

      在主线程生成并启动了子线程,需要用到子线程返回的结果,也就是需要主线程需要在子线程结束后再结束,这时候就要用到 join() 方法。

      System.out.println(Thread.currentThread().getName() + "线程运行开始!");
      Thread6 thread1 = new Thread6();
      thread1.setName("线程 B");
      thread1.join();
      System.out.println("这时 thread1 执行完毕之后才能执行主线程");

      2.1.4、线程唤醒(notify)

      Object 类中的 notify() 方法,唤醒在此对象监视器上等待的单个线程,如果所有线程都在此对象上等待,则会选择唤醒其中一个线程,选择是任意的,并在对实现做出决定时发生,线程通过调用其中一个 wait() 方法,在对象的监视器上等待,直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程,被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争。类似的方法还有 notifyAll() ,唤醒再次监视器上等待的所有线程。

      2.2、Thread基本方法

      2.2.1、线程睡眠(sleep)

      sleep 导致当前线程休眠,与 wait 方法不同的是 sleep 不会释放当前占有的锁,sleep(long)会导致线程进入 TIMED-WATING 状态,而 wait()方法会导致当前线程进入 WATING 状态。

      2.2.2、 线程中断(interrupt)

      中断一个线程,其本意是给这个线程一个通知信号,会影响这个线程内部的一个中断标识位。这个线程本身并不会因此而改变状态(如阻塞,终止等)。

      • 调用 interrupt()方法并不会中断一个正在运行的线程。也就是说处于 Running 状态的线程并不会因为被中断而被终止,仅仅改变了内部维护的中断标识位而已。
      • 若调用 sleep()而使线程处于 TIMED-WATING 状态,这时调用 interrupt()方法,会抛出InterruptedException,从而使线程提前结束 TIMED-WATING 状态。
      • 许多声明抛出 InterruptedException 的方法(如 Thread.sleep(long mills 方法)),抛出异常前,都会清除中断标识位,所以抛出异常后,调用 isInterrupted()方法将会返回 false。
      • 中断状态是线程固有的一个标识位,可以通过此标识位安全的终止线程。比如,你想终止一个线程 thread 的时候,可以调用 thread.interrupt()方法,在线程的 run 方法内部可以根据 thread.isInterrupted()的值来优雅的终止线程。

      2.2.3、其他方法

      • isAlive(): 判断一个线程是否存活。
      • activeCount(): 程序中活跃的线程数。
      • enumerate(): 枚举程序中的线程。
      • currentThread(): 得到当前线程。
      • isDaemon(): 一个线程是否为守护线程。
      • setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
      • setName(): 为线程设置一个名称。
      • setPriority(): 设置一个线程的优先级。
      • getPriority()::获得一个线程的优先级。

      2.3、sleep与wait区别

      对于 sleep()方法,我们首先要知道该方法是属于 Thread 类中的。而 wait()方法,则是属于 Object 类中的。sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。 在调用 sleep()方法的过程中,线程不会释放对象锁。而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

      三、单线程实现

      3.1、继承Thread类

      Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的 start()实例方法。

      start()方法是一个 native 方法,它将启动一个新线程,并执行 run()方法。

      public class MyThread extends Thread {
      public void run() {}
      }

      System.out.println("MyThread.run()");
      MyThread myThread1 = new MyThread();
      myThread1.start();

      3.2、实现Runnable接口

      如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个 Runnable 接口。

      public class MyThreadRunnable implements Runnable {
      public void run() { }
      }

      //启动 MyThread,需要首先实例化一个 Thread,并传入自己的 MyThread 实例:
      MyThread myThread = new MyThread();
      Thread thread = new Thread(myThread);
      thread.start();
      //事实上,当传入一个 Runnable target 参数给 Thread 后,Thread 的 run()方法就会调用
      target.run()

      3.3、start与run区别

      1. start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕,可以直接继续执行下面的代码。
      2. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行;
      3. 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。

      3.4、“每任务每线程”的程序缺点

      1. 线程生命周期的开销,如果请求是频繁且轻量级的,就会消耗大量的计算资源。
      2. 资源消耗量,主要是针对内存。如果可运行的线程数多于可用的处理器数,线程将会空闲。大量空闲线程占用更多的内存,给GC带来压力,而且会存在大量线程在竞争CPU资源,还会产生其他的性能开销。如果有足够多的线程保持所有CPU忙碌,那么再创建更多的线程是百害无一利的。
      3. 稳定性,应该限制可创建线程的数目,这个数目受OS、JVM启动参数、Thread的构造函数中请求的栈大小等因素影响。如果打破了这些规则可能会收到OutOfMemoryError错误。(在32位的机器上,主要的限制因素是线程栈的地址空间,每个线程都维护着两个执行栈,一个用于java代码,另一个用于原生代码,典型的JVM默认会产生一个组合的栈,大小在0.5M大小左右,可以通过-Xss JVM参数或者通过Thread的构造函数修改这个值。如果为每个线程分配了大小232字节的栈,那么你的线程数量将被限制在几千到几万间不等)。

      这种实现方式只能当作练习,不能应用到正式的环境中去。

      四、线程池实现

      4.1、基本原理

      Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable 和 Future、FutureTask 这几个类。

      jvm专题(4) - 【2/3】多线程-实现

      4.1.1、线程池的原理

      线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。他的主要特点为:线程复用;控制最大并发数;管理线程。

      每一个 Thread 的类都有一个 start 方法。 当调用 start 启动线程时 Java 虚拟机会调用该类的 run方法。 那么该类的 run() 方法中就是调用了 Runnable 对象的 run() 方法。 我们可以继承重写Thread 类,在其 start 方法中添加不断循环调用传递过来的 Runnable 对象。 这就是线程池的实现原理。循环方法中不断获取 Runnable 是用 Queue 实现的,在获取下一个 Runnable 之前可以是阻塞的。

      4.2.2、线程池的组成

      1. 线程池管理器:用于创建并管理线程池
      2. 工作线程:线程池中的线程
      3. 任务接口:每个任务必须实现的接口,用于工作线程调度其运行
      4. 任务队列:用于存放待处理的任务,提供一种缓冲机制

      4.3.3、线程池的执行策略

      将任务的提交与任务的执行进行解耦,价值在于让你可以简单地为一个类给定的任务制定执行策略,并且保证后续的修改不至于太困难,执行策略指明了:

      1. 任务在什么线程中执行;
      2. 任务以什么顺序执行(FIFO、LIFO、优先级);
      3. 可以有多少个任务并发执行;
      4. 可以有多少个任务进入等待执行队列;
      5. 如果系统过载,需要放弃哪个任务并且如何通知Application知道这一切;
      6. 在一个任务的执行前与后,应该插入什么处理操作。

      执行策略是资源管理工具,最佳策略取决于可用的计算资源和你对服务质量的需求,将任务的提交与执行进行分离,有助于在部署阶段选择一个与当前硬件最匹配的执行策略。所以以后的程序中尽量少用或不用new Thread(runnable).start()这种方式而改用Executor委托执行。

      4.4.4、线程池工作过程

      jvm专题(4) - 【2/3】多线程-实现

      1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
      2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
      1. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
      2.  如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
      3. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
      4. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常 RejectExecutionException。
      1. 当一个线程完成任务时,它会从队列中取下一个任务来执行。
      2. 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

      4.2、Executor

      线程是使任务异步执行的机制,作为Executor框架的一部分,它是基于生产—消费模式设计,java.util.concurrent提供了一个灵活的线程池实现。Executor很简单但可用于异步任务执行,支持不同类型的任务执行策略,还为任务提交和任务执行之间的解耦提供了标准的方法,还提供了对生命周期的支持以及钩子函数等。在生产—消费模式中,提交任务是执行者是生产者,执行任务的线程是消费者。Executor接口的实现相当于一个模板或抽象模板实现,是把Runnable委托给Executor来执行。所以可以定义自己的Executor实现类。

      Executor实现通常只是为执行任务而创建线程,JVM会在所有线程全部终止后才通出,因此无法正确关闭Executor,进而会阻塞JVM的结束。

      因为Executor是异步地执行任务,所以有很多不确定因素,为了解决执行服务的生命周期问题,提供了ExecutorService接口,它扩展了Executor接口同时添加了一些用于生命周期(运行、关闭、终止)管理的方法。

      public interface ExecutorService extends Executor {
      //不在接收新的任务后平缓关闭
      void shutdown();
      //强制停止正在运行和队列中等待的任务,并返回列表以便序列化等操作,下次启动时恢复
      List<Runnable> shutdownNow();
      boolean isShutdown();
      boolean isTerminated();
      //转换ExecutorService的状态,同时调用shutdown()方法
      boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
      }

      一旦所有的任务全部完成后ExecutorService会转入终止状态,可以调用awaitTermination等待ExecutorService到达终止状态,也可以轮询isTerminated判断ExecutorService是否已经终止。通常shutdown会紧随awaitTermination之后,这样可以产生同步关闭ExecutorService的效果。

      4.2.1、基本线程池实现(Runnable)

      ExecutorService threadPool = Executors.newFixedThreadPool(10);
      while (true) {
      threadPool.execute(new Runnable() { });// 提交多个线程任务,并执行

      @Override
      public void run () { }
      System.out.println(Thread.currentThread().getName() + " is running ..");
      try {
      Thread.sleep(3000);
      } catch (InterruptedException e) {
      e.printStackTrace();
      }
      }

      4.2.2、带返回值的线程(Callable, Future)

      Callable主要用于计算时间长的任务,比如下载、复杂运算、数据库操作。它会在主进入点call--等待返回值,并为可能抛出的异常预告做好了准备,Executors包含一些工具方法,可以把其他类型的任务封装成一个Callable。Callable<void>===Runnable。在Executor框架中,如果任务执行要花费很长时间,任务可以手动取消,但对于已经开始的任务,只有中断后才可以取消,取消一个已完成的任务不会有任何影响。

      Future可以描述任务的生命周期,并提供了相关的方法来获得任务的结果、取消任务以及检验任务是否完成。Future和ExecutorService这些线程相关的对象的生命周期是单向的,无法回退。Future的get方法是个阻塞方法, 注意get处理异常的能力,它会把异常重新包装后再抛出。

      ExecutorService的所有submit()方法都返回一个Future。可以将一个Runnable或Callable提交给executor,然后得到一个Future。也可以显示的为Runnable或Callable创建一个FutureTask。FutureTask实现了Runnable接口。所以可以直接交给ExecutorService来执行,也可以直接调用run方法。

      private final ExecutorService executor = Executors.newCachedThreadPool();

      void renderPage(CharSequence source) {
      final List<ImageInfo> imageInfos = scanForImageInfo(source);
      Callable<List<ImageData>> task =
      new Callable<List<ImageData>>() {
      public List<ImageData> call() {
      List<ImageData> result = new
      ArrayList<ImageData>();
      for (ImageInfo imageInfo : imageInfos)
      result.add(imageInfo.downloadImage());
      return result;
      }
      };

      Future<List<ImageData>> future = executor.submit(task);
      // FutureTask future = new FutureTask(task);
      // executor.submit(future);//这两行代码和上面一行代码是等价的。

      try {
      List<ImageData> imageData = future.get();
      for (ImageData data : imageData)
      renderImage(data); //渲染图片
      } catch (InterruptedException e) {
      // 重新声明线程的中断状态
      Thread.currentThread().interrupt();
      // 不需要结果 ,故取消任务
      future.cancel(true);//异常处理
      } catch (ExecutionException e) {
      throw launderThrowable(e.getCause());
      }
      }

      4.3、ThreadPoolExecutor

      4.3.1、线程池类型

      public ThreadPoolExecutor(int corePoolSize,
      int maximumPoolSize,
      long keepAliveTime,
      TimeUnit unit,
      BlockingQueue<Runnable> workQueue) {
      this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
      Executors.defaultThreadFactory(),
      defaultHandler);
      }
      1. corePoolSize:指定了线程池中的线程数量。
      2. maximumPoolSize:指定了线程池中的最大线程数量。
      3. keepAliveTime:当前线程池数量超过 corePoolSize 时,多余的空闲线程的存活时间,即多次时间内会
      4. unit:keepAliveTime 的单位。
      5. workQueue:任务队列,被提交但尚未被执行的任务。
      6. threadFactory:线程工厂,用于创建线程,一般用默认的即可。 7. handler:拒绝策略,当任务太多来不及处理,如何拒绝任务。
      4.3.1.1、newCachedThreadPool(非定长)

      创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。调用 execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。因此,长时间保持空闲的线程池不会使用任何资源。

      4.3.1.2、newFixedThreadPool(定长)

      创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。在某个线程被显式地关闭之前,池中的线程将一直存在。

      4.3.1.3、newScheduledThreadPool(定长周期)

      创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行支持定时的以及周期性的任务执行,类似于Timer。

      4.3.1.4、newSingleThreadExecutor(单线程)

      Executors.newSingleThreadExecutor()返回一个线程池(这个线程池只有一个线程),这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去!

      4.4、拒绝策略

      线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也 塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。JDK 内置的拒绝策略如下:

      1. AbortPolicy : 直接抛出异常,阻止系统正常运行。
      2. CallerRunsPolicy : 只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的 任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
      3. DiscardOldestPolicy : 丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再 次提交当前任务。
      4. DiscardPolicy : 该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢 失,这是最好的一种方案。

      以上内置拒绝策略均实现了 RejectedExecutionHandler 接口,若以上策略仍无法满足实际需要,完全可以自己扩展 RejectedExecutionHandler 接口。

      4.5、阻塞队列

      阻塞队列,关键字是阻塞,先理解阻塞的含义,在阻塞队列中,线程阻塞有这样的两种情况:1、当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放 入队列;2、当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有 空的位置,线程被自动唤醒。

      4.5.1、ArrayBlockingQueue(公平、非公平)

      用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。默认情况下不保证访问者公平的访问队列,所谓公平访问队列是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列,即先阻塞的生产者线程,可以先往队列里插入元素,先阻塞的消费者线程,可以先从队列里获取元素。通常情况下为了保证公平性会降低吞吐量。我们可以使用以下代码创建一个公平的阻塞队列

      4.5.2、LinkedBlockingQueue(两个独立锁提高并发)

      基于链表的阻塞队列,同 ArrayListBlockingQueue 类似,此队列按照先进先出(FIFO)的原则对元素进行排序。而 LinkedBlockingQueue 之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。

      4.5.3、PriorityBlockingQueue(compareTo 排序实现优先)

      是一个支持优先级的无界队列。默认情况下元素采取自然顺序升序排列。可以自定义实现compareTo()方法来指定元素进行排序规则,或者初始化 PriorityBlockingQueue 时,指定构造参数 Comparator 来对元素进行排序。需要注意的是不能保证同优先级元素的顺序。

      4.5.4、DelayQueue(缓存失效、定时任务 )

      是一个支持延时获取元素的无界阻塞队列。队列使用 PriorityQueue 来实现。队列中的元素必须实现 Delayed 接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。我们可以将 DelayQueue 运用在以下应用场景:

      1. 缓存系统的设计:可以用 DelayQueue 保存缓存元素的有效期,使用一个线程循环查询DelayQueue,一旦能从 DelayQueue 中获取元素时,表示缓存有效期到了;
      2. 定时任务调度:使用 DelayQueue 保存当天将会执行的任务和执行时间,一旦从DelayQueue 中获取到任务就开始执行,从比如 TimerQueue 就是使用 DelayQueue 实现的。
      4.5.5、SynchronousQueue(不存储数据、可用于传递数据)

      是一个不存储元素的阻塞队列。每一个 put 操作必须等待一个 take 操作,否则不能继续添加元素。SynchronousQueue 可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用,SynchronousQueue 的吞吐量高于 LinkedBlockingQueue 和ArrayBlockingQueue。

      4.5.6、LinkedTransferQueue(无阻塞)

      是一个由链表结构组成的无界阻塞 TransferQueue 队列。相对于其他阻塞队列,LinkedTransferQueue 多了 tryTransfer 和 transfer 方法。

      1. transfer 方法:如果当前有消费者正在等待接收元素(消费者使用 take()方法或带时间限制的poll()方法时),transfer 方法可以把生产者传入的元素立刻 transfer(传输)给消费者。如果没有消费者在等待接收元素,transfer 方法会将元素存放在队列的 tail 节点,并等到该元素;
      2. tryTransfer 方法。则是用来试探下生产者传入的元素是否能直接传给消费者。如果没有消费者等待接收元素,则返回 false。和 transfer 方法的区别是 tryTransfer 方法无论消费者是否接收,方法立即返回。而 transfer 方法是必须等到消费者消费了才返回。对于带有时间限制的 tryTransfer(E e, long timeout, TimeUnit unit)方法,则是试图把生产者传入的元素直接传给消费者,但是如果没有消费者消费该元素则等待指定的时间再返回,如果超时还没消费元素,则返回 false,如果在超时时间内消费了元素,则返回 true。
      4.5.7、LinkedBlockingDeque(双向阻塞)

      是一个由链表结构组成的双向阻塞队列。所谓双向队列指的你可以从队列的两端插入和移出元素。双端队列因为多了一个操作队列的入口,在多线程同时入队时,也就减少了一半的竞争。相比其他的阻塞队列,LinkedBlockingDeque 多了 addFirst,addLast,offerFirst,offerLast,peekFirst,peekLast 等方法,以 First 单词结尾的方法,表示插入,获取(peek)或移除双端队列的第一个元素。以 Last 单词结尾的方法,表示插入,获取或移除双端队列的最后一个元素。另外插入方法 add 等同于 addLast,移除方法 remove 等效于 removeFirst。但是 take 方法却等同于 takeFirst,不知道是不是 Jdk 的 bug,使用时还是用带有 First 和 Last 后缀的方法更清楚。在初始化 LinkedBlockingDeque 时可以设置容量防止其过渡膨胀。另外双向阻塞队列可以运用在“工作窃取”模式中。


      End,下一章节笔者打算就线程间的协同机制(锁)进行详细描述,因为锁的内容比较多,敬请期待。

      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://blog.51cto.com/arch/5316924,作者:生而为人我很遗憾,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:Java学习之继承中成员方法的访问特点

      下一篇:Java的Object类九大方法

      相关文章

      2025-05-06 09:19:12

      Spring多线程事务 能否保证事务的一致性(同时提交、同时回滚)?

      Spring的事务信息是存在ThreadLocal中的Connection, 所以一个线程永远只能有一个事务

      2025-05-06 09:19:12
      Spring , 事务 , 多线程 , 线程
      2025-04-14 08:45:56

      java学习第十一天笔记-字符串222-学生管理系统4查询

      java学习第十一天笔记-字符串222-学生管理系统4查询

      2025-04-14 08:45:56
      i++ , java , jvm , System , 学习
      2025-04-11 07:08:33

      Java线程中的run()和start()区别

      Java线程中的run()和start()区别

      2025-04-11 07:08:33
      run , start , 启动 , 多线程 , 方法 , 线程 , 运行
      2025-04-09 09:16:00

      Java线程的基础概念介绍(结合代码说明)

      线程是操作系统能够进行运算调度的最小单位,它是进程中的实际运作单位。每个线程执行的都是某一个进程的代码的某个片段。

      2025-04-09 09:16:00
      CPU , 多线程 , 方法 , 状态 , 线程 , 进程 , 阻塞
      2025-04-01 10:28:48

      java中satb和tlab有什么区别?

      java中satb和tlab有什么区别?

      2025-04-01 10:28:48
      Java , jvm , JVM , 内存空间
      2025-03-27 09:34:39

      阻塞与唤醒:多线程编程的神秘面纱

      在多线程编程中,线程状态切换是一个非常关键的概念。了解线程状态切换的原理,对于编写高效、稳定的多线程程序至关重要。

      2025-03-27 09:34:39
      切换 , 多线程 , 状态 , 等待 , 线程
      2025-03-26 08:57:33

      三种方法教你实现多线程交替打印ABC,干货满满!

      假设有三个线程,分别打印字母A、B、C。我们需要让这三个线程交替运行,按顺序打印出“ABCABCABC...”,直到打印一定次数或者满足某个条件。如何通过多线程的协调实现这个任务呢?这听起来简单,实际涉及到线程之间的同步和互斥,是我们学习多线程编程的一个很好的练习。

      2025-03-26 08:57:33
      Condition , wait , 信号量 , 多线程 , 线程
      2025-03-21 09:33:29

      C运行时库(C Run-Time Libraries)

      C运行时库(C Run-Time Libraries)

      2025-03-21 09:33:29
      DLL , lib , link , 多线程 , 链接 , 静态
      2025-03-21 08:23:07

      深入理解Java中的多线程编程

      在本文中,我们将探讨Java多线程编程的核心概念和实践。我们将从基本概念开始,逐步深入到线程的创建与管理、同步与锁机制以及高级并发工具的应用。通过实例代码和详细解释,帮助读者全面掌握Java多线程编程的精髓。

      2025-03-21 08:23:07
      Java , Runnable , 同步 , 多线程 , 并发 , 线程 , 编程
      2025-03-18 09:59:32

      深入学习Java语言核心技术

      深入学习Java语言核心技术

      2025-03-18 09:59:32
      Java , JVM , 多线程 , 并发 , 框架 , 线程 , 集合
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5232074

      查看更多

      最新文章

      Spring多线程事务 能否保证事务的一致性(同时提交、同时回滚)?

      2025-05-06 09:19:12

      Java线程中的run()和start()区别

      2025-04-11 07:08:33

      Java线程的基础概念介绍(结合代码说明)

      2025-04-09 09:16:00

      java中satb和tlab有什么区别?

      2025-04-01 10:28:48

      阻塞与唤醒:多线程编程的神秘面纱

      2025-03-27 09:34:39

      三种方法教你实现多线程交替打印ABC,干货满满!

      2025-03-26 08:57:33

      查看更多

      热门文章

      JAVA多线程学习笔记

      2023-05-11 06:05:48

      Thrift第七课 服务器多线程发送异常

      2023-05-16 09:42:24

      synchronized实现两个线程交替运行

      2022-12-28 07:22:30

      线程池笔记(一)

      2022-12-28 07:22:30

      【多线程】synchronized 中的 锁优化的机制 (偏向锁->轻量级锁->重量级锁)

      2023-04-13 09:26:52

      【java基础】ArrayList源码解析

      2023-07-03 08:06:07

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      阻塞与唤醒:多线程编程的神秘面纱

      多线程之并发容器一

      java中垃圾回收器让工作线程停顿下来是怎么做的?

      多线程(1)

      python3-多线程示例

      【python】python进程、线程、协程和什么时候使用

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号