阿里巴巴Java开发手册重点解读(一)

来源:互联网 发布:什么软件防蓝光好 编辑:程序博客网 时间:2024/05/16 16:04

编程规约-OOP规约-8

【强制】关于基本数据类型与包装数据类型的使用标准如下:
1) 所有的 POJO 类属性必须使用包装数据类型。
2) RPC 方法的返回值和参数必须使用包装数据类型。
3) 所有的局部变量【推荐】 使用基本数据类型。

这些类型的对象常作为查询对象或者结果对象,基本数据类型的默认值都是0,而引用类型的默认值为null。在拼接sql时或者显示结果时,null有明确的含义,可以不拼接或者不显示,但是0的含义有二义性,是没有值还是真的为0?

编程规约-OOP规约-9

【强制】定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值

同理。pojo作为查询对象或者更新对象时,对根据条件自动拼接sql来说,是一种伤害。原文中已经解释比较清楚了。

反例: POJO 类的 gmtCreate 默认值为 new Date();但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。

编程规约-并发处理-3&4

3【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明: 使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

也就是不允许new Thread(…).start()这种形式来创建和启动线程。线程是稀缺资源,创建和销毁都有代价。线程池将线程和任务分开,无论多少任务,都可以用可控制的线程条数来执行,线程得以被重用。同时,不会因为任务的暴增导致线程数的暴增,线程数的暴增会引起CPU过载,很有可能造成系统假死,甚至造成jvm外别的应用程序不响应。

4【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明: Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM。

Executors是一个工具类,它有几个便捷的静态方法可以帮我们创建不同特性的线程池,但是这些线程池都有风险。这些便捷方法,其实都是帮我们定好了ThreadPoolExecutor的构造参数,返回结果也是ThreadPoolExecutor的实例。
ThreadPoolExecutor的构造参数需要精心设计,具体问题具体分析,才能满足我们的需求,其参数含义是
int corePoolSize,核心线程数,线程池维护的最小线程数
int maximumPoolSize,最多线程数
long keepAliveTime,核心线程数之外创建的线程从空闲到消亡的等待时间
TimeUnit unit,上一个参数的时间单位
BlockingQueue workQueue,等待队列,任务数太多,超出核心线程中可用线程,也就是核心线程满足不了持续增长的任务时,新任务被放入此队列,此队列满,将会扩大线程数,直到线程数达到max的限定
RejectedExecutionHandler handler,核心线程繁忙不能执行新任务,新任务进队列,队列满,新增线程直到总线程数到达maximumPoolSize,所有线程都繁忙,这时还在提交新任务,就会调用这个拒绝handler来实施拒绝策略了。

需要注意的问题:
1. 如果等待队列是无界队列,那么maximumPoolSize是不起作用的,后面的拒绝策略也是不起作用的,假设任务增长速度非常快,队列具备无限扩容能力,内存很快就会被撑爆。这就是FixedThreadPool和SingleThreadPool的问题所在。
2. CachedThreadPool倒不是用无界队列,它用了一个SynchronousQueue队列,这个队列就是个假队列,不能放任何东西,你可以认为容量为0,那么任何超出的任务都会导致新增线程,线程总数受max限制,然而它的上限被设为多少呢?Integer.MAX_VALUE。假设任务增长速度非常快,那么这个线程池就会不停新建线程来处理新任务。这会导致CPU过载,和内存溢出。
3. ScheduledThreadPool类似。

一个良好设计的线程池是这样的:
1. 核心数和平均任务数对应
2. 偶尔抖动,任务数增加,使用有界队列(ArrayBlockingQueue设置大小)来缓冲任务
3. max不要太大,比核心数大一些就可以了,前面还有缓冲队列呢。和核心数设置为一样也可以
4. 时间的设计按默认的来就行
5. 可以有拒绝策略,为什么不能呢?计算机资源是有限的,处理不了的时候抛出异常总比撑爆CPU和内存要好。jdk提供了四种内置策略

a.AbortPolicy,抛出RejectedExecutionException
b.CallerRunsPolicy,尝试直接调用任务的run方法
c.DiscardOldestPolicy,将丢弃最老的等待中的任务,尝试执行当前任务
d.DiscardPolicy,悄悄地丢弃,不抛任何异常

个人觉得,抛异常提示下任务提交方,是不错的选择。

下次我们聊聊其他问题。

原创粉丝点击