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. 【强制】线程资源必须通过线程池提供,不允许在应⽤中⾃⾏显式创建线程。
说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开 销,解决资源不⾜的问题。 如果不使⽤线程池,有可能造成系统创建⼤量同类 线程⽽导致消耗完内存或者“过度切换”的问题。
4. 【强制】线程池不允许使⽤ Executors 去创建,⽽是通过 ThreadPoolExecutor 的⽅式,这 样的处理⽅式让写的同学更加明确线程池的运⾏规则,规避资源耗 尽的⻛险。
说明:Executors 返回的线程池对象的弊端如下: FixedThreadPool 和 SingleThreadPool:允许的请求队列⻓度为 Integer.MAX_VALUE,可能会堆积⼤量的请求,从⽽导致 OOM。 CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建 ⼤量的线程,从⽽导致 OOM。
5. 【强制】SimpleDateFormat 是线程不安全的类,⼀般不要定义为 static 变量, 如果定义为 static,必须加锁,或者使⽤ DateUtils ⼯具类
6. 【强制】必须回收⾃定义的 ThreadLocal 变量,尤其在线程池场景下,线程经 常会被复⽤, 如果不清理⾃定义的 ThreadLocal 变量,可能会影响后续业务逻 辑和造成内存泄露等问题。 尽量在代理中使⽤ try-finally 块进⾏回收。
正例:
objectThreadLocal.set(userInfo);
try {
// ...
} finally {
objectThreadLocal.remove();
}
7.【强制】在使⽤阻塞等待获取锁的⽅式中,必须在 try 代码块之外,并且在加锁⽅ 法与 try 代 码块之间没有任何可能抛出异常的⽅法调⽤,避免加锁成功后,在 finally 中⽆法解锁。
说明⼀:如果在 lock ⽅法与 try 代码块之间的⽅法调⽤抛出异常,那么⽆法解 锁,造成其它线程⽆法成功 获取锁
说明⼆:如果 lock ⽅法在 try 代码块之内,可能由于其它⽅法抛出异常,导致在 finally 代码块中,unlock 对未加锁的对象解锁,它会调⽤ AQS 的 tryRelease ⽅法(取决于具体实现类),抛出 IllegalMonitorStateException 异常。
说明三:在 Lock 对象的 lock ⽅法实现中可能抛出 unchecked 异常,产⽣的后 果与说明⼆相同
正例:
Lock lock = new XxxLock();
// ...
lock.lock();
try {
doSomething(); doOthers();
} finally {
lock.unlock();
}
反例:
Lock lock = new XxxLock();
// ...
try {
// 如果此处抛出异常,则直接执⾏ finally 代码块
doSomething();
// ⽆论加锁是否成功,finally 代码块都会执⾏
lock.lock();
doOthers();
} finally {
lock.unlock();
}
8. 【强制】在使⽤尝试机制来获取锁的⽅式中,进⼊业务代码块之前,必须先判 断当前线程是否 持有锁。锁的释放规则与锁的阻塞等待⽅式相同。
说明:Lock 对象的 unlock ⽅法在执⾏时,它会调⽤ AQS 的 tryRelease ⽅法(取 决于具体实现类),如果 当前线程不持有锁,则抛出 IllegalMonitorStateException 异常。
正例:
Lock lock = new XxxLock();
// ...
boolean isLocked = lock.tryLock();
if (isLocked) {
try {
doSomething();
doOthers();
} finally {
lock.unlock();
} }