阿里巴巴Java开发规范——编程规约(4)

阿里巴巴Java开发规范——编程规约(4)

编程规约

(六)并发处理

1. 【强制】获取单例对象需要保证线程安全,其中的方法也要保证线程安全。

说明:资源驱动类、工具类、单例工厂类都需要注意。

2. 【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。

正例:

public class TimerTaskThread extends Thread {public TimerTaskThread() {super.setName("TimerTaskThread");...}
}
3. 【强制】线程资源必须通过线程池供,不允许在应用中自行显式创建线程。

说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决
资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或
者“过度切换”的问题。

4. 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

说明:Executors 返回的线程池对象的弊端如下:
1)FixedThreadPool 和SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2)CachedThreadPool 和ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

虽然Executors类提供的便捷方法(如newFixedThreadPool, newCachedThreadPool等)易于使用,但它们往往隐藏了配置细节,可能导致资源耗尽或性能问题。因此,直接使用ThreadPoolExecutor来创建线程池能提供更细粒度的控制和更高的灵活性,是推荐的做法。

正例:使用ThreadPoolExecutor创建线程池

下面是使用ThreadPoolExecutor直接创建线程池的一个示例,这样可以明确指定线程池的参数,如核心线程数、最大线程数、空闲线程存活时间、任务队列类型及容量等,从而更好地控制资源使用和性能表现。

import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;public class CustomThreadPoolExample {public static void main(String[] args) {// 核心线程数int corePoolSize = 5;// 最大线程数int maximumPoolSize = 10;// 空闲线程存活时间long keepAliveTime = 60L;// 时间单位TimeUnit unit = TimeUnit.SECONDS;// 任务队列,这里使用无界队列,可根据实际情况选择有界队列如ArrayBlockingQueue以避免资源耗尽LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();// 创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue);// 设置线程池拒绝策略,当队列满且线程数达到最大时的处理方式executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 这里选择抛出异常,也可选择其他策略// 提交任务到线程池执行的逻辑同上例...// 关闭线程池的逻辑也同上例...}
}

在这个示例中,我们直接通过ThreadPoolExecutor构造函数指定了线程池的关键参数:

  • corePoolSize:线程池的基本大小,即使没有任务执行时也会保持这么多线程。
  • maximumPoolSize:线程池能够容纳的最大线程数。
  • keepAliveTime:多余的空闲线程等待新任务的最长时间。
  • unitkeepAliveTime的时间单位。
  • workQueue:用于保存等待执行的任务的阻塞队列,这里使用的是无界队列LinkedBlockingQueue,但在实际应用中应谨慎选择,以防止因队列无限增长导致内存溢出。

通过这样的方式,开发者能够更清晰地理解线程池的工作原理和配置,从而更好地根据实际需求调整参数,避免资源耗尽风险。

5. 【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。

正例:注意线程安全,使用 DateUtils。亦推荐如下处理:

private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {@Overrideprotected DateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd");}
};

说明:如果是JDK8 的应用,可以使用Instant 代替Date,LocalDateTime 代替Calendar,
DateTimeFormatter 代替SimpleDateFormat,官方给出的解释:simple beautiful strong
immutable thread-safe。
总结来说,为了确保线程安全,避免将SimpleDateFormat定义为静态变量,或者通过同步、使用ThreadLocal、采用线程安全的库类等方式来解决潜在的并发问题。

6. 【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。

在高并发场景下,锁的使用会对性能产生显著影响,因此选择合适的同步策略至关重要。遵循“锁的最低限度原则”,即尽可能减少锁的使用和降低其带来的性能损耗,是优化并发性能的关键。以下是几个具体的建议:

  1. 优先考虑无锁数据结构:无锁编程通过使用原子变量(如AtomicInteger, AtomicReference等)或CAS(Compare-and-Swap)操作,可以在不需要显式加锁的情况下实现线程安全。例如,使用AtomicInteger进行计数操作,相比使用synchronizedReentrantLock通常能提供更好的性能。

  2. 细粒度锁:尽量减少锁的范围,只在必要的代码块上加锁,而不是整个方法。这样可以减少锁的持有时间,提高并发度。例如,如果一个方法中有多个独立的资源需要修改,应该分别对这些资源加锁,而不是对整个方法加锁。

  3. 对象锁优于类锁:对象锁(即synchronized作用于实例方法或对象实例上的代码块)通常比类锁(即synchronized作用于静态方法或类对象上的代码块)具有更高的并发能力。因为类锁会限制所有实例对共享资源的访问,而对象锁仅限制同一实例上的访问。因此,如果可能,应优先使用对象锁。

  4. 使用并发容器:Java并发包(java.util.concurrent)提供了许多高性能的并发容器,如ConcurrentHashMap, CopyOnWriteArrayList等,这些容器内部已经实现了高效的锁机制或无锁算法,能有效减少锁竞争,提高并发处理能力。

  5. 锁升级与降级:对于ReentrantLock和一些高级并发容器,它们支持锁的升级与降级机制,如从无锁状态升级到偏向锁、轻量级锁,再到重量级锁,以及相应的降级过程,以适应不同的竞争情况,减少不必要的开销。

  6. 考虑使用乐观锁:在某些场景下,如果数据冲突的概率较低,可以使用乐观锁策略,如通过版本号或时间戳来判断数据是否被其他线程修改过,从而减少锁的使用。

总之,在设计高并发系统时,深入分析业务场景,选择最适合的并发控制策略,合理使用锁,甚至避免使用锁,是提高系统吞吐量和响应速度的关键。

7. 【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。

说明:线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序
也必须是A、B、C,否则可能出现死锁。

8. 【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。

说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次
数不得小于3 次。

乐观锁常见的实现机制包括版本号(versioning)、时间戳(timestamp)或使用CAS(Compare and Swap)操作等。在每次数据更新时,都会检查数据的版本号或者时间戳是否与读取时一致,如果不一致,则拒绝本次更新。

9. 【强制】多线程并行处理定时任务时,Timer 运行多个TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。

在Java中,Timer类和ScheduledExecutorService都是用于执行定时任务或周期性任务的工具,但它们在异常处理和任务执行的健壮性上有明显的区别。下面通过对比两个示例来说明为什么在多线程并行处理定时任务时,推荐使用ScheduledExecutorService而非Timer

使用Timer的示例

当使用Timer时,如果定时任务抛出了未捕获的异常,整个Timer线程将会终止,导致所有定时任务停止执行。

import java.util.Timer;
import java.util.TimerTask;public class TimerExample {public static void main(String[] args) {Timer timer = new Timer();TimerTask task1 = new TimerTask() {@Overridepublic void run() {System.out.println("Task 1 executing...");throw new RuntimeException("Task 1 failed!"); // 未被捕获的异常}};TimerTask task2 = new TimerTask() {@Overridepublic void run() {System.out.println("Task 2 executing...");}};// 安排任务执行timer.schedule(task1, 1000);timer.schedule(task2, 2000); // 当task1抛出异常后,task2不会执行}
}

在上述代码中,当Task 1运行时抛出异常,Timer线程会终止,导致Task 2无法按计划执行。

使用ScheduledExecutorService的示例

相比之下,ScheduledExecutorService提供了更好的异常处理机制,即使某个任务抛出异常,也不会影响其他任务的执行。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledExecutorServiceExample {public static void main(String[] args) {ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);Runnable task1 = () -> {System.out.println("Task 1 executing...");throw new RuntimeException("Task 1 failed!"); // 未被捕获的异常};Runnable task2 = () -> {System.out.println("Task 2 executing...");};// 安排任务执行executor.schedule(task1, 1, TimeUnit.SECONDS);executor.schedule(task2, 2, TimeUnit.SECONDS); // 即使task1抛出异常,task2仍会执行// 注意:在实际应用中,建议添加适当的异常处理逻辑,比如使用Future来捕获异常}
}

在这个例子中,即使Task 1抛出了异常,由于ScheduledExecutorService为每个任务分配了独立的线程(或复用了线程池中的线程),所以Task 2依然能够正常执行。此外,ScheduledExecutorService提供了更灵活的线程管理和任务调度功能,更适合复杂的并发任务处理场景。

10. 【推荐】使用CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行至await 方法,直到超时才返回结果。

说明:注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。

11. 【参考】volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。

如果是count++操作,使用如下类实现

AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 
//如果是JDK8,推荐使用LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/8261.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

有没有国内个人可用的GPT平替?推荐5个AI工具

随着AI技术的快速发展&#xff0c;AI写作正成为创作的新风口。但是面对GPT-4这样的国际巨头&#xff0c;国内很多小伙伴往往望而却步&#xff0c;究其原因&#xff0c;就是它的使用门槛高&#xff0c;还有成本的考量。 不过&#xff0c;随着GPT技术的火热&#xff0c;国内也涌…

MY SQL 实验一:

一、实验目的 通过实验了解MYSQL数据库服务器的基本架构及基本的使用方法。 二、实验原理、条件 本实验采用著名的开源数据库软件MYSQL 作为实验平台。MYSQL有多种版本&#xff0c;常用的是服务器版。数据库引擎是用于存储、处理和保护数据的核心服务。MYSQL有多个数据库引擎&a…

苍穹外卖面试总结

项目介绍 我完成了一个外卖项目&#xff0c;名叫苍穹外卖&#xff0c;是跟着黑马程序员的课程来自己动手写的。 项目基本实现了外卖客户端、商家端的后端完整业务。 商家端分为员工管理、文件上传、菜品管理、分类管理、套餐管理、店铺营业状态、订单下单派送等的管理、数据…

2024数学-微积分和线性代数/本科研究生专业考试/考研/论文/重点公式考点汇总/最难公式投票

## 整体公式汇总列表 http://www.deepnlp.org/equation/category/math #### 微积分 ## 几何级数http://www.deepnlp.org/equation/arithmetic-and-geometric-progressions ## 级数收敛http://www.deepnlp.org/equation/convergence-of-series ## 二项式展开 http://www.dee…

Elasticsearch:开启高效搜索与分析的新篇章

Elasticsearch&#xff1a;开启高效搜索与分析的新篇章 文章目录 Elasticsearch&#xff1a;开启高效搜索与分析的新篇章前言一、Elasticsearch的起源与特点起源特点1、分布式架构&#xff1a;2、实时性&#xff1a;3、全文搜索&#xff1a;4、可扩展性&#xff1a;5、数据类型…

电商店铺经验分享,抖店/视频号小店统统适用(2024新版)

我是王路飞。 如果你已经开通了抖店或者视频号小店&#xff0c; 但是做了几天发现做不起来/不好做。 不要担心&#xff0c;也不要放弃。 既然已经选择了电商这条路&#xff0c;就要对项目有信心&#xff0c;对自己有信心。 我给你们分享一些我做电商的一些踩坑经验&#xf…

自主抓取IMAP流量

自主抓取IMAP流量 根据测试需求&#xff0c;需要抓取IMAP流量包&#xff0c;使用wireshark Python&#xff08;IMAP库&#xff09;实现 实现Python 代码 不废话直接上&#xff1b; 注意别用SSL&#xff0c;这个是加密无法被抓包&#xff1b;port&#xff1a;143是公开&…

深入浅出 BERT

Transformer 用于学习句子中的长距离依赖关系&#xff0c;同时执行序列到序列的建模。 它通过解决可变长度输入、并行化、梯度消失或爆炸、数据规模巨大等问题&#xff0c;比其他模型表现更好。使用的注意力机制是神经架构的一部分&#xff0c;使其能够动态突出显示输入数据的…

WMS仓储管理系统库存分类的详细讲解

在当今日益复杂和快速变化的商业环境中&#xff0c;仓库管理成为了一个企业不可或缺的关键环节。WMS仓储管理系统解决方案凭借其自动化和信息化的优势&#xff0c;为企业带来了革命性的改变&#xff0c;特别是在库存分类方面。接下来&#xff0c;我们将深入探讨WMS仓储管理系统…

关于windows系统上tcp TcpAckFrequency TcpDelAckTicks 注册表参数的社区回复

您好&#xff01;很高兴为您解答关于Windows操作系统中TCP/IP设置的问题。 在Windows的注册表设置中&#xff0c;TcpAckFrequency和TcpDelAckTicks是两个关于TCP确认&#xff08;ACK&#xff09;包发送策略的参数。这两个参数控制的是延迟确认&#xff08;Delayed ACK&#xf…

前端动画总结

前端动画 一、css动画 transition 过渡 transition:transiton-property,transition-duration,transition-timing-function,transition-delay相关属性说明 属性默认值其他说明property过渡的属性all不是所有css属性都支持过渡duration动画完成时间0s单位是秒timing-functio…

实用的Chrome命令

常用命令&#xff1a; 如下为常用的chrome命令&#xff0c;欢迎尝试体验。 1. chrome://downloads 查看下载内容 2. chrome://extensions 查看扩展 3. chrome://plugins 显示已安装插件 4. chrome://bookmarks 书签管理器 5. chrome://history 历史直接访问 6. chrome://res…

智慧农业可视化,探索未来农业的新天地

在科技日新月异的今天&#xff0c;农业领域也迎来了翻天覆地的变化。不再只是面朝黄土背朝天&#xff0c;现代科技与农业的结合正在逐步改变着我们的耕种方式。 一、智慧农业&#xff0c;未来已来 步入智慧农业展馆&#xff0c;仿佛进入了一个科幻世界。看似复杂的农业数据&am…

API开发淘宝(京东)API接口:获取淘宝京东等平台数据的api接口分享

接口应用场景——电商产品定价 电商平台产品的定价问题是很多品牌非常重视的一个问题&#xff0c;产品的定价取决于很多因素&#xff0c;包括成本、供需情况、促销策略及竞争对手的价格等。因此&#xff0c;想要更合理地定价&#xff0c;品牌需要获取到影响产品定价的各类数据&…

速盾:高防ip和高防cdn有什么相同点?

高防IP&#xff08;Dedicated IP&#xff09;和高防CDN&#xff08;Content Delivery Network&#xff09;都是用来保护网站免受各种网络攻击的技术手段&#xff0c;它们在一定程度上具有相同的作用和效果。下面将详细介绍它们的相同点。 首先&#xff0c;高防IP和高防CDN都能…

Windows下 nignx启动报nginx: [emerg] bind() to 0.0.0.0:80 failed (10013:

1、Windows下 nignx启动报nginx: [emerg] bind() to 0.0.0.0:80 failed (10013: An attempt was made to access a socket in a way forbidden by its access permissions) 解决办法&#xff1a; &#xff08;原因是端口被占用了&#xff09; 1、找到nginx.conf文件 2、修改…

618热门好物大盘点,省心购物指南快看过来!

在618购物节即将拉开帷幕之际&#xff0c;整个互联网仿佛都弥漫着一种节日的热闹与期待。各大品牌纷纷亮出他们的杀手锏&#xff0c;推出了一系列诱人的优惠活动和特色产品&#xff0c;让人眼花缭乱&#xff0c;心动不已。如果你此刻正犹豫着该把哪一件宝贝收入囊中&#xff0c…

C++-9

C 1.已知C风格的字符串&#xff0c;完成对字符串通过下标访问时的异常处理机制(越界访问) 2.写一个程序&#xff0c;程序包含两个类&#xff0c;类中实现一个成员函数&#xff0c;MyGetChar(), 类A中每调用一 次&#xff0c;按顺序得到一个数字字符&#xff0c;比如第-次调用得…

VTK结合QT显示单个和多个点云

我的代码主要时将单个和多个点云使用VTK和QT显示&#xff0c;运用QVTKWidget控件。 显示单个点云 //单个点云可视化 void Visualize(pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud2){vtkSmartPointer<vtkPoints> m_points vtkSmartPointer<vtkPoints>::N…

浏览器的使用心得和探索

文章目录 前言一、浏览器二、个人推荐2.1 3602.2 猎豹2.3 火狐2.4 chorme2.5 Opera2.6 QQ浏览器2.7 猫眼&#xff08;Catsxp&#xff09;2.8 edge 三、Browser plugin3.1 AdGuard 广告拦截器3.2 Axure RP 查看器3.3 Edge深度清理者3.4 FeHelper(前端助手)3.5 MarkDownload - Ma…