阿里巴巴Java 开发手册编程规约之并发处理

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。

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

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

说明: 如果是 JDK8 的应用,可以使用 Instant 代替 Date, LocalDateTime 代替 Calendar,DateTimeFormatter代替Simpledateformatter,官方给出的解释:simple beautiful strong immutable thread-safe。

6. 【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁; 能锁区块,就不要锁整个方法体; 能用对象锁,就不要用类锁。
说明: 尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法。
7. 【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
说明: 线程一需要对表 A、 B、 C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A、 B、 C,否则可能出现死锁。

8. 【强制】并发修改同一记录时,避免更新丢失, 需要加锁。 要么在应用层加锁, 要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。
说明: 如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。
9. 【强制】多线程并行处理定时任务时, Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。
10. 【推荐】使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown方法,线程执行代码注意 catch 异常,确保 countDown 方法可以执行,避免主线程无法执行至 await 方法,直到超时才返回结果。
说明: 注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。
11. 【推荐】避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降。
说明: Random 实例包括 java.util.Random 的实例或者 Math.random()的方式。
正例: 在 JDK7 之后,可以直接使用 API ThreadLocalRandom, 而在 JDK7 之前, 需要编码保证每个线程持有一个实例。
12. 【推荐】 在并发场景下, 通过双重检查锁(double-checked locking) 实现延迟初始化的优化问题隐患(可参考 The "Double-Checked Locking is Broken" Declaration), 推荐问题解决方案中较为简单一种(适用于 JDK5 及以上版本) ,将目标属性声明为 volatile 型。
反例:

class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        if (helper == null) synchronized(this) {
            if (helper == null)
                helper = new Helper();
        }
    return helper;
    }
    // other functions and members...
}

13. 【参考】 volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。如果是 count++操作,使用如下类实现:
AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数) 。
14. 【参考】 HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在开发过程中可以使用其它数据结构或加锁来规避此风险。

15. 【参考】 ThreadLocal 无法解决共享对象的更新问题, ThreadLocal 对象建议使用 static修饰。这个变量是针对一个线程内所有操作共有的,所以设置为静态变量,所有此类实例共享此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。

时间: 2024-11-09 18:45:00

阿里巴巴Java 开发手册编程规约之并发处理的相关文章

阿里巴巴Java 开发手册编程规约之异常日志

(一)异常处理1. [强制]Java 类库中定义的一类 RuntimeException 可以通过预先检查进行规避,而不应该通过 catch 来处理,比如: IndexOutOfBoundsException, NullPointerException 等等.说明: 无法通过预检查的异常除外,如在解析一个外部传来的字符串形式数字时,通过 catchNumberFormatException 来实现.正例: if (obj != null) {...}反例: try { obj.method()

阿里巴巴Java 开发手册编程规约之工程结构

(一)应用分层1. [推荐]图中默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于Web 层,也可以直接依赖于 Service 层,依此类推: 开放接口层:可直接封装 Service 方法暴露成 RPC 接口: 通过 Web 封装成 http 接口: 进行网关安全控制. 流量控制等.终端显示层:各个端的模板渲染并执行显示的层. 当前主要是 velocity 渲染, JS 渲染,JSP 渲染,移动端展示等.Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简

阿里巴巴Java 开发手册编程规约之OOP规约

1. [强制]避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可.2. [强制]所有的覆写方法,必须加@Override 注解.说明: getObject()与 get0bject()的问题.一个是字母的 O,一个是数字的 0,加@Override可以准确判断是否覆盖成功.另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错.3. [强制] 相同参数类型,相同业务含义,才可以使用 Java 的可变参数,避免使用 Object.说明: 可变

阿里巴巴Java 开发手册编程规约之MySQL 数据库

(一)建表规约1. [强制]表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint(1 表示是, 0 表示否) .说明: 任何字段如果为非负数,必须是 unsigned.正例: 表达逻辑删除的字段名 is_deleted, 1 表示删除, 0 表示未删除.2. [强制]表名.字段名必须使用小写字母或数字, 禁止出现数字开头,禁止两个下划线中间只出现数字.数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑.正例: getter_

阿里巴巴Java 开发手册编程规约之命名风格

1.[强制]代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束.这两个符号有特殊用途,代理类等~反例:_name/__name/$Object/name_/name$/Object$2.[强制]代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式.说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义.注意,即使纯拼音命名方式也要避免采用.正例:alibaba/taobao/youku/hangzhou 等国际通用的名称,可视同英文.反例:DaZheProm

阿里巴巴Java 开发手册编程规约之控制语句

1. [强制]在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止: 在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有.2. [强制]在 if/else/for/while/do 语句中必须使用大括号. 即使只有一行代码,避免使用单行的形式: if (condition) statements;3. [推荐] 表达异常的分支时, 少用 if-else 方式,

阿里巴巴Java 开发手册编程规约之集合处理

1. [强制] 关于 hashCode 和 equals 的处理,遵循如下规则:1) 只要重写 equals,就必须重写 hashCode.2) 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法.3) 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals.说明: String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key

阿里巴巴Java 开发手册编程规约之常量定义

1. [强制]不允许任何魔法值(即未经定义的常量) 直接出现在代码中.反例: String key = "Id#taobao_" + tradeId;cache.put(key, value);2. [强制] long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字1 混淆,造成误解.说明: Long a = 2l; 写的是数字的 21,还是 Long 型的 2?3. [推荐]不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护.如:缓存相

阿里巴巴Java 开发手册编程规约之代码格式

1. [强制]大括号的使用约定.如果是大括号内为空,则简洁地写成{}即可,不需要换行: 如果是非空代码块则:1)左大括号前不换行.2)左大括号后换行.3)右大括号前换行.4)右大括号后还有 else 等代码则不换行: 表示终止的右大括号后必须换行.2. [强制] 左小括号和字符之间不出现空格: 同样,右小括号和字符之间也不出现空格.详见第 5 条下方正例提示.反例: if (空格a == b空格)3. [强制] if/for/while/switch/do 等保留字与括号之间都必须加空格.4.