最近阿里发布了一个插件p3c,用于进行Java开发规约的检查扫描。由于插件的代码是开源,于是第一时间也翻查了代码,发现目前实现的检查规则主要在/p3c-pmd/src/main/resources/rulesets
、/idea-plugin/p3c-common/src/main/kotlin/com/alibaba/p3c/idea/inspection/standalone
目录下。将规则大致看了下,这里将自己平时开发不太注意但它提到的几点记录一下。
平时由于某些原因,不会删除代码,而只是临时将代码注释起来,应按照规范留下注释原因。For codes which are temporarily removed and likely to be reused, use /// to add a reasonable note.
代码示例:
public static void hello() {
/// Business is stopped temporarily by the owner.
// Business business = new Business();
// business.active();
System.out.println("it's finished");
}
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样 的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
平时在开发中,不要手动地创建线程,最好使用线程池,并且线程要命名。
有两种办法创建线程池:编码法、配置法。
编码法:
// use org.apache.commons.lang3.concurrent.BasicThreadFactory
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(
1,
new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
或
// use com.google.common.util.concurrent.ThreadFactoryBuilder
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
//Common Thread Pool
ExecutorService pool = new ThreadPoolExecutor(
5,
200,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024),
namedThreadFactory,
new ThreadPoolExecutor.AbortPolicy());
配置法:
<bean id="threadFactory" class="org.springframework.scheduling.concurrent.CustomizableThreadFactory">
<constructor-arg value="Custom-prefix-"/>
<property name="daemon" value="true"/>
</bean>
<bean id="rejectedExecutionHandler" class="java.util.concurrent.ThreadPoolExecutor.AbortPolicy">
</bean>
<bean id="userThreadPool"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="queueCapacity" value="2000" />
<property name="threadFactory" ref="threadFactory"/>
<property name="rejectedExecutionHandler" ref="rejectedExecutionHandler"/>
</bean>
在代码里使用:
userThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
由于SimpleDateFormat是非线程安全的,不要定义一个静态的SimpleDateFormat属性,然后调用其format方法。有3种方法避免这种并发冲突:
private static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
public String getFormat(Date date){
SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT);
return sdf.format(date);
}
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void getFormat(){
synchronized (sdf){
sdf.format(new Date());
}
…;
}
private static final ThreadLocal<DateFormat> DATE_FORMATTER = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。
public class UserHolder {
private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<User>();
public static void set(User user){
userThreadLocal.set(user);
}
public static User get(){
return userThreadLocal.get();
}
public static void remove(){
userThreadLocal.remove();
}
}
public class UserInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
UserHolder.set(new User());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.remove();
}
}
避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed 导致的性能下降。 说明:Random 实例包括 java.util.Random 的实例或者 Math.random()的方式。 正例:在 JDK7 之后,可以直接使用 API ThreadLocalRandom,而在 JDK7 之前,需要编码保 证每个线程持有一个实例。
@Service
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {
@Override
public void save(User user) {
//some code
//db operation
}
}
@Service
public class UserServiceImpl implements UserService {
@Override
@Transactional(rollbackFor = Exception.class)
public void save(User user) {
//some code
//db operation
}
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private DataSourceTransactionManager transactionManager;
@Override
@Transactional
public void save(User user) throws UserException {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can only be done programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// execute your business logic here
//db operation
transactionManager.commit(status);
} catch (Exception ex) {
transactionManager.rollback(status);
throw new UserException(ex);
}
}
}
Apache BeanUtils性能较差,可以使用其他方案比如Spring BeanUtils, Cglib BeanCopier。
获取当前毫秒数:System.currentTimeMillis(); 而不是new Date().getTime();
1 | Integer[] b = (Integer [])c.toArray(new Integer[c.size()]); |
---|
map_key_value_nullable
上面只列了一些个人平时没在注意的地方,完整的编码规约见阿里巴巴Java开发手册。另外网上还有一个白话版。