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

阿里开发规约之编程规约(4)

前言哈喽,大家好,我是Java选手牛皮糖。本周也是个值得兴奋的日子,没有征兆的下起雪来了。下了一整天的雪,可惜是在上班&#

前言

哈喽,大家好,我是Java选手牛皮糖。本周也是个值得兴奋的日子,没有征兆的下起雪来了。下了一整天的雪,可惜是在上班,不然定要约上三五好友去搓上一顿火锅。吃着火锅唱着歌,赏着雪不要太爽。
在这里插入图片描述


正文

上回一块学习了项目中十分常用的集合处理等,那这回我们就一块来看看并发处理


并发处理

1、 【强制】获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
说明:资源驱动类、工具类、单例工厂类都需要注意。
单例相关:
定义:单例类只允许一个实例存在。
适用场景:


  • 需要生成唯一序列的环境
  • 需要频繁实例化然后销毁的对象。
  • 创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
  • 方便资源相互通信的环境。

项目中使用的场景:


  • 工具类。
  • 配置文件。

2、【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。
正例:自定义线程工厂,并且根据外部特征进行分组,比如,来自同一机房的调用,把机房编号赋值给
whatFeatureOfGroup

public class UserThreadFactory implements ThreadFactory { private final String namePrefix;
private final AtomicInteger nextId = new AtomicInteger(1);
// 定义线程组名称,在利用 jstack 来排查问题时,非常有帮助
UserThreadFactory(String whatFeatureOfGroup) {
namePrefix = "From UserThreadFactory's " + whatFeatureOfGroup + "-Worker-";
}
@Override
public Thread newThread(Runnable task) {
String name = namePrefix + nextId.getAndIncrement(); Thread thread = new Thread(null, task, name, 0, false); System.out.println(thread.getName());
return thread;
} }

3、 【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。 如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
创建和销毁线程是非常浪费资源的事情,内存资源对Java来说是十分重要的,因此在使用线程的时候让线程池帮助解决这个问题。

4、【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这 样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:


  1. FixedThreadPool 和 SingleThreadPool:
    允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
  2. CachedThreadPool:
    允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
    Executors部分源码

public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());}

5、【强制】SimpleDateFormat 是线程不安全的类&#xff0c;一般不要定义为 static 变量&#xff0c;如果定义为 static&#xff0c;
必须加锁&#xff0c;或者使用 DateUtils 工具类。
正例:注意线程安全&#xff0c;使用 DateUtils。亦推荐如下处理:

private static final ThreadLocal<DateFormat> df &#61; new ThreadLocal<DateFormat>(){ &#64;Overrideprotected DateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd");
} };

static背景


static只能修饰内部类&#xff0c;且static在内存中只存储一份数据。被static修饰的内部类或变量是属于这个类的&#xff0c;而不是实例的。因此多线程情况下会存在数据不一致的问题。


SimpleDateFormat背景


SimpleDateFormat类中通过变量calendar存储时间&#xff0c;且SimpleDateFormat是线程不安全的类。如果使用static修饰SimpleDateFormat&#xff0c;则calendar将变成全局变量&#xff0c;则当多个线程同时进行SimpleDateFormat#parse、SimpleDateFormat#format方法时&#xff0c;就会出现线程不安全问题。


6、 【强制】必须回收自定义的 ThreadLocal 变量&#xff0c;尤其在线程池场景下&#xff0c;线程经常会被复用&#xff0c; 如果不清理自定义的 ThreadLocal 变量&#xff0c;可能会影响后续业务逻辑和造成内存泄露等问题。 尽量在代理中使用 try-finally 块进行回收。
正例:
objectThreadLocal.set(userInfo); try {
// …
} finally { objectThreadLocal.remove();
}
ThreadLocal背景


ThreadLocal是解决线程不安全的一种方式。它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题&#xff0c;是属于线程的。当线程结束的时候&#xff0c;ThreadLocal也会被回收&#xff0c;如果线程被复用了&#xff0c;则ThreadLocal不会被回收就造成了内存泄露。


7、【强制】高并发时&#xff0c;同步调用应该去考量锁的性能损耗。能用无锁数据结构&#xff0c;就不要用锁;能 锁区块&#xff0c;就不要锁整个方法体;能用对象锁&#xff0c;就不要用类锁。
说明:尽可能使加锁的代码块工作量尽可能的小&#xff0c;避免在锁代码块中调用 RPC 方法。最小加锁原则。
8. 【强制】对多个资源、数据库表、对象同时加锁时&#xff0c;需要保持一致的加锁顺序&#xff0c;否则可能会造 成死锁。
说明:线程一需要对表 A、B、C 依次全部加锁后才可以进行更新操作&#xff0c;那么线程二的加锁顺序也必须是 A、 B、C&#xff0c;否则可能出现死锁。
9. 【强制】在使用阻塞等待获取锁的方式中&#xff0c;必须在 try 代码块之外&#xff0c;并且在加锁方法与 try 代 码块之间没有任何可能抛出异常的方法调用&#xff0c;避免加锁成功后&#xff0c;在 finally 中无法解锁。
说明一:如果在 lock 方法与 try 代码块之间的方法调用抛出异常&#xff0c;那么无法解锁&#xff0c;造成其它线程无法成功 获取锁。
说明二:如果 lock 方法在 try 代码块之内&#xff0c;可能由于其它方法抛出异常&#xff0c;导致在 finally 代码块中&#xff0c;unlock 对未加锁的对象解锁&#xff0c;它会调用 AQS 的 tryRelease 方法(取决于具体实现类)&#xff0c;抛出 IllegalMonitorStateException 异常。
说明三:在 Lock 对象的 lock 方法实现中可能抛出 unchecked 异常&#xff0c;产生的后果与说明二相同。

正例:
Lock lock &#61; new XxxLock(); // ...
lock.lock();
try {
doSomething();
doOthers(); } finally {
lock.unlock(); }
反例:
Lock lock &#61; new XxxLock(); // ...
try {
// 如果此处抛出异常&#xff0c;则直接执行 finally 代码块 doSomething();
// 无论加锁是否成功&#xff0c;finally 代码块都会执行 lock.lock();
doOthers();
} finally { lock.unlock();
}

10.【强制】在使用尝试机制来获取锁的方式中&#xff0c;进入业务代码块之前&#xff0c;必须先判断当前线程是否
持有锁。锁的释放规则与锁的阻塞等待方式相同。
说明:Lock 对象的 unlock 方法在执行时&#xff0c;它会调用 AQS 的 tryRelease 方法(取决于具体实现类)&#xff0c;如果
当前线程不持有锁&#xff0c;则抛出 IllegalMonitorStateException 异常。

正例
Lock lock &#61; new XxxLock();
// ...
boolean isLocked &#61; lock.tryLock(); if (isLocked) {
try { doSomething();
doOthers(); } finally {
lock.unlock(); }
}

11.【强制】并发修改同一记录时&#xff0c;避免更新丢失&#xff0c;需要加锁。要么在应用层加锁&#xff0c;要么在缓存加锁&#xff0c;要么在数据库层使用乐观锁&#xff0c;使用 version 作为更新依据。
说明:如果每次访问冲突概率小于 20%&#xff0c;推荐使用乐观锁&#xff0c;否则使用悲观锁。乐观锁的重试次数不得小于 3 次。


乐观锁&#xff1a;每次写数据的时候&#xff0c;认为不会被别的线程操作&#xff0c;但是更新的时候会判断下数据有没有被改过。&#xff08;适用于多读少写的场景&#xff09;
悲观锁&#xff1a;每次写数据都认为会有别的线程操作&#xff0c;然后加锁。别的线程会一直被阻塞&#xff0c;直到拿到锁为止。&#xff08;适用于一致性比较高的场景)


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


总结

高并发场景一致是项目中的难点&#xff0c;同时也是面试中面试官最喜欢面的地方。上文中每条规则都可以延伸很多&#xff0c;限于篇幅&#xff0c;这里就不做过多的分析。后面抽时间在和大家深入分下高并发的场景。


推荐阅读
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 本文讨论了编写可保护的代码的重要性,包括提高代码的可读性、可调试性和直观性。同时介绍了优化代码的方法,如代码格式化、解释函数和提炼函数等。还提到了一些常见的坏代码味道,如不规范的命名、重复代码、过长的函数和参数列表等。最后,介绍了如何处理数据泥团和进行函数重构,以提高代码质量和可维护性。 ... [详细]
author-avatar
书友48919914
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有