热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

java线程亲缘性_亲缘性线程池,这是什么鬼?

一、前言JDK中的线程池主要解决两个问题:一方面当执行大量异步任务时候线程池能够提供较好的性能,在不使用线程池的时,每当需要执行异步任务时

一、前言

JDK中的线程池主要解决两个问题:

一方面当执行大量异步任务时候线程池能够提供较好的性能,在不使用线程池的时,每当需要执行异步任务时候是直接 new一线程运行,而线程的创建和销毁是需要开销的。而使用线程池时候,线程池里面的线程是可复用的,不会每次执行异步任务时候都重新创建和销毁线程。

另一方面线程池提供了一种资源限制和管理的手段,比如可以限制线程的个数,动态新增线程等,每个 ThreadPoolExecutor 也保留了一些基本的统计数据,比如当前线程池完成的任务数目等。

JDK中的线程池固然好,但是其不具有亲缘性,也就是当我们顺序向其中投递多个任务后,不能保证具有相同属性的任务顺序执行,本文我们就来看一个可以实现亲缘性的线程池。

二、测试案例

首先我们在做个测试,看看JDK中线程池是否具有亲缘性,我们创建一个Person类,其中id作为唯一标识,data为需要处理的数据,如下代码,我们创建一些Person对象,放到list,然后把任务顺序投递到JDK线程池:

//0.普通线程池

static ExecutorService executorService = Executors.newFixedThreadPool(8);

public static void executeByOldPool(List

personList.stream().forEach(p -> executorService.execute(() -> {

System.out.println(JSON.toJSONString(p));

}));

}

public static void main(String[] args) {

//1.创建列表

List

personList.add(Person.builder().id(1).data("1s").build());

personList.add(Person.builder().id(2).data("2s").build());

personList.add(Person.builder().id(1).data("11s").build());

personList.add(Person.builder().id(3).data("3s").build());

personList.add(Person.builder().id(1).data("111s").build());

personList.add(Person.builder().id(2).data("22s").build());

personList.add(Person.builder().id(3).data("33s").build());

personList.add(Person.builder().id(1).data("1111s").build());

//2.使用普通线程池执行

executeByOldPool(personList);

}

执行上面代码,如果线程池是亲缘的,则比如对应id=1的Person的输出应该是和投递到线程池时一致,也就是下面顺序:

{"data":"1s","id":1}

{"data":"11s","id":1}

{"data":"111s","id":1}

{"data":"1111s","id":1}

但是当我们执行上面代码,一个可能的输出为:

{"data":"3s","id":3}

{"data":"2s","id":2}

{"data":"33s","id":3}

{"data":"22s","id":2}

{"data":"1111s","id":1}

{"data":"1s","id":1}

{"data":"11s","id":1}

{"data":"111s","id":1}

可知其并没实现亲缘性,比如id=1的person的data并没有按照投递线程池顺序输出。

究其原因是因为JDK中线程池是不保证先投递到线程池的任务先执行完毕。

三、亲缘性线程池实现

首先我们需要引入其依赖:

然后上面executeByOldPool方法修改为下面:

static KeyAffinityExecutor executor = KeyAffinityExecutor.newSerializingExecutor(8,200, "MY-POOL");

public static void executeByAffinitydPool(List

personList.stream().forEach(p -> executor.executeEx(p.getId(), () -> {

System.out.println(JSON.toJSONString(p));

}));

}

如上代码投递任务到线程池时,我们使用person的id作为key,这可以保证相同的id顺序投递到线程池的任务可以顺序执行,修改后,运行,一个可能的输出为:

{"data":"3s","id":3}

{"data":"1s","id":1}

{"data":"2s","id":2}

{"data":"33s","id":3}

{"data":"11s","id":1}

{"data":"22s","id":2}

{"data":"111s","id":1}

{"data":"1111s","id":1}

如上输出可知对应相同id的Person,其输出与投递到线程池顺序一致。

那么亲缘性线程池如何实现保证顺序内,大家可以看下其代码,其实很简单,就是把相同key的任务按照投递线程池的顺序,放到同一个内存队列(这里我们设置为200大小),每个内存队列有一个线程来消费。那么消费线程有几个那?其实是按照创建线程池时newSerializingExecutor的第一个参数来决定。

四、总结

亲缘性线程池在需要保证顺序消费,并且需要高吞吐量的情况下很用用,必须普通情况下顺序消费的保证是靠单线程来做的(比如rocketmq的顺序消息,消费端消费时)。

更多技术分享,请扫描关注微信公众号:

file0人点赞博文

d0c1501a6d8bb921cf36400dc89de69f.png



推荐阅读
  • 本文介绍了在实现了System.Collections.Generic.IDictionary接口的泛型字典类中如何使用foreach循环来枚举字典中的键值对。同时还讨论了非泛型字典类和泛型字典类在foreach循环中使用的不同类型,以及使用KeyValuePair类型在foreach循环中枚举泛型字典类的优势。阅读本文可以帮助您更好地理解泛型字典类的使用和性能优化。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • 本文介绍了如何使用OpenXML按页码访问文档内容,以及在处理分页符和XML元素时的一些挑战。同时,还讨论了基于页面的引用框架的局限性和超越基于页面的引用框架的方法。最后,给出了一个使用C#的示例代码来按页码访问OpenXML内容的方法。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了如何使用JSONObiect和Gson相关方法实现json数据与kotlin对象的相互转换。首先解释了JSON的概念和数据格式,然后详细介绍了相关API,包括JSONObject和Gson的使用方法。接着讲解了如何将json格式的字符串转换为kotlin对象或List,以及如何将kotlin对象转换为json字符串。最后提到了使用Map封装json对象的特殊情况。文章还对JSON和XML进行了比较,指出了JSON的优势和缺点。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 可空类型可空类型主要用于参数类型声明和函数返回值声明。主要的两种形式如下: ... [详细]
author-avatar
NAVETEX
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有