超越线程池:Java并发并没有你想的那么糟糕

简介:

很多人一直唠叨着并发中的新概念。然而,许多开发人员还没有机会把过多的注意力都放在上面。在这篇文章中,我们将带您了解Java 8 streams、 Hadoop、 Apache Spark、 Quasar fibers以及响应式编程,让你迅速入门。尤其是如果你不经常用它们的话。一句话,它并不遥远,它就在我们身边。

超越线程池:Java并发并没有你想的那么糟糕

我们该怎么做?

谈到并发,一种很好的方式来形容当前的问题是来回答几个小问题以便更好的了解它:

它是一个数据处理任务么?如果是这样的话,它可以分解为独立的任务单元么?

操作系统、虚拟机和你的代码之间的关系是什么?(本地线程 VS 轻量级线程)

有多少机器和处理器参与?(单核 VS 多核)

让我们带着问题,一起找出每个问题的最佳答案吧。

1、从线程池到并行流

在Java 8中,我们了解到新的流API接口,它允许应用聚集操作,如筛选、排序或者映射数据流。流允许我们做的另一件事情是,在多核机器上应用并行操作。并行流 ——通过把Fork/Join框架引入Java 7将线程间的工作分离。Java 6并发库,我们看到了ExecutorService创建和处理我们的工作线程池,这不得不说是个进步。

Fork/Join也建立在ExecutorService之上,与传统的线程主要的区别在于如何在线程和支持多核的机器间分配工作。用一个简单的 ExecutorService你能完全控制工作线程之间的负载分布,确立每个任务的大小以便线程来处理。而Fork/Join,恰好有个work-stealing算法分配线程间的负载。简而言之,这允许大型任务可以被分成更小单元,并在不同的线程间处理,最终我们可以知道——它是为了平衡线程间的 工作。然而,这并不是万能的。

有时并行流会减慢你速度的,所以你需要多想想。在你的方法中使用parallelStream会导致瓶颈和减速(在我们基准测试中跑慢了约15%左右)。假设我们已经运行多个线程,在其中一些我们使用parallelStream,在线程池中添加越来越多的线程。这可以很容易超过我们的核心处理能力,由于增加了上下文转换一切都慢下来了。

小结:在单机上并行流使线程处理抽象化,在一定程度上这会均衡核心间的负载。然而,如果你想高效使用它们,记住硬件是关键而不是生产更多的线程而超出机器的处理能力。

2、Apache Hadoop和Apache Spark

接下来谈多核机器、 PB级数据和任务,这跟所有从twitter提到的Java或重载机器学习算法类似。谈到Hadoop,不得不说这个应用广泛的框架及它的组 件:Hadoop分布式文件系统(HDFS)、资源管理平台(YARN)、数据处理模块(MapReduce)和其他所需的类库和工具(Common)。 在这些组件上层还有一些其他很受欢迎的可选工具,比如运行在HDFS上的数据库(HBase)、查询语言平台(Pig)和数据仓库基础结构(Hive)。

Apache Spark 作为一种新数据处理模块,以内存性能和快速执行的弹性分布式数据集(RDDs)而出名,不同于不能高效使用内存和磁盘的Hadoop MapReduce。Databricks公布的最新标准显示当用少于10倍节点的时候,对1PB数据的排序Spark比Hadoop快三倍。

典型的Hadoop用例在于查询数据,而Spark正以其快速的机器学习算法越来越出名。但这只是冰山一角,Databricks如是说:“Spark 使应用程序在Hadoop集群中运行在内存中快100倍,当运行在磁盘中时甚至快10倍”。

小结:Spark是在Hadoop生态系统中的后起之秀,有一个常见的误解是我们现在经常谈它一些不合作或竞争的事情,但是我认为我们在这正在看到这个框架的发展。

3、Quasar fibers

我们有机会运行在Hadoop,现在让我们回到单机。事实上,在Java多线程应用程序和集中在单线程上,让我们眼光再长远些。就我们而言,HotSpot JVM线程与本地系统线程相同,持有一个线程并且运行在”虚拟“线程中,这在fibers中都包含的。Java没有原生的fibers支持,但是不要担 心,Quasar通过Parallel Universe解决了我们的问题。

Quasar 是一个开源的JVM库。它支持fibers(也称为轻量级线程),并且还充当框架的角色,在后面中我会提到。在这上下文转换是它本质的名字。当我们核心数 量有限,一旦本地线程数量越大我们就会收到越来越多的上下文开销。一种解决这个问题的方式是fibers,使用单线程支持”多线程“。这看起来像threadcepiton的一个实例。

Fibers还可以被视为一个从线程池的进化,当我们通过应用并行流的时候避开了线程过载的危险。他们更容易衡量线程和允许令人可观的并行”轻量“线程数量。它们不是为了取代线程,而是应该用在那些相对来说经常堵塞的代码中,就如同担任真正异步线程的角色。

小结:并行领域在Java并发性中正提供一种新的思路,虽然还没有版本发布,但是值得一试。

4、Actor和响应式编程

在响应式的官方言论中,最新的释义有4原则:响应、有弹性、灵活性和消息驱动。这基本意味着快速、容错、可伸缩的和支持非阻塞通信。

让我们看看Akka Actor是如果支持它的吧。简单来讲Actor有一个状态和一个特定的行为,通过交换消息沟通彼此的邮箱。一个Actor系统作为一个整体应该被每个应 用程序创建,拥有一个层次结构将任务分解成更小的任务以便每个角色最多只有一个监督的角色。一个角色也可以处理这个任务,通过委托给另一个角色将其进一步 分解或在实例失败的情况下,将它反馈给它的监督者。无论哪种方式,消息不应该包括行为或者共享可变的状态,每个角色都有一个独立的状态和行为。

它是一个从大多数开发者在使用的并发模型的思考模式的转移。尽管它起源于70年代,但是为了适应现代应用程序的要求,直到最近几年它才复苏。并行领域的Quasar也支持Actor,实现的主要区别在于fibers/轻量级线程。

小结:相反的,Actor模型需要管理线程池,让它远离使用工具包。今天面对这种应用程序处理的问题,尤其在我们可以处理拥有更多核心的高并发系统方面又重新有了关注。

总结

关于使用并发或者并行算法,我们今天通过介绍4种方法来解决问题来应对你需要的场景。希望这有 助于激起你的兴趣,以及在这大谈并发话题的现在开拓下你的视野。超越线程池,有一种将这委托给语言及它的工具的趋势——关注新的技术并应用它而不是花费无 数个小时解决竞态条件和锁。


来源:51CTO

相关文章
|
8天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
10天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
|
10天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。
|
10天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
33 3
|
10天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
91 2
|
18天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
46 6
|
2月前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
1月前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
2月前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
27天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####