翻译了阿里巴巴Java编码指南中涉及的54条规则
并发
[强制]定制的ThreadLocal变量必须回收,特别是在使用线程池(其中线程经常被重用)时。否则,它可能会影响后续的业务逻辑,并导致内存泄漏等意外问题。
[强制]一个有意义的线程名称有助于跟踪错误信息,因此在创建线程或线程池时分配一个名称。
案例:
1
2
3public class TimerTaskThread extends Thread {
public TimerTaskThread(){
super.setName("TimerTaskThread"); … }[强制]线程应该由线程池提供。不允许显式地创建线程。注意:使用线程池可以减少创建和销毁线程的时间,节省系统资源。如果我们不使用线程池,将会创建许多类似的线程,从而导致“内存耗尽”或过度切换问题。
线程池应该由ThreadPoolExecutor而不是executor创建。这样就可以理解线程池的参数。它还将减少耗尽系统资源的风险。注意:以下是使用executor创建线程池所产生的问题:
FixedThreadPool和SingleThreadPool:最大请求队列大小。大量的请求可能会导致OOM。
CachedThreadPool ScheduledThreadPool:
允许创建的线程数为Integer.MAX_VALUE。创建太多的线程可能会导致OOM
[强制]SimpleDataFormat是不安全的,不要将其定义为静态变量。如果必须使用,则必须使用lock或Apache DateUtils类。正例:使用DateUtils时要注意线程安全。建议使用如下:
1
2
3
4
5
6private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};注意:在JDK8中,可以使用Instant代替Date;同样地,Calendar被LocalDateTime取代,SimpleDateFormatter被DateTimeFormatter取代。
[强制]使用ScheduledExecutorService而不是Timer来运行多个timeask,因为Timer将在捕获异常失败的情况下杀死所有正在运行的线程。
[推荐]当使用CountDownLatch将异步操作转换为同步操作时,每个线程退出前必须调用countdown方法。确保在线程运行期间捕获任何异常,以便执行倒计时方法。如果主线程无法到达await方法,程序将返回,直到超时。注意:子线程抛出的异常不能被主线程捕获。
[推荐]避免多线程使用随机实例。尽管它是线程安全的,但在同一种子上的竞争会损害性能。注意:随机实例包括java.util.Random和Math.random()的实例。
正面例子:
JDK7之后,可以直接使用ThreadLocalRandom API。但是在JDK7之前,需要在每个线程中创建实例。