Java线程池

从JDK1.5开始,Java API提供了Executor框架,创建不同的线程池。有单线程池、数目固定的线程池、缓存线程池
Executors线程池工厂提供了一些创建不同特性线程池ThreadPoolExecutor的方法,它们都返回ExecutorService对象:

  • Executors.newSingleThreadPool()
  • Executors.newFixedThreadPool(int size):定长线程池,可控制线程最大并发数,超出的线程在队列中等待。
  • Executors.newCachedTheadPool():适合很多生存期短的任务,空闲线程会在指定时间内被回收。
  • Executors.newScheduledThreadPool():定长线程池,支持定时及周期性任务执行

它们内部都是封装了ThreadPoolExecutor:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 15, TimeUnit.SECONDS,new ArrayBlockingQueue(5), new ThreadPoolExecutor.CallerRunsPolicy());

线程池超载后的拒绝策略

池中的线程用光,等待队列也已排满。

扩展线程池

ThreadPoolExecutor提供了用于扩展的接口,比如要监控每个任务执行的开始和结束时间。

打印异常堆栈是排查问题的指南针,用线程池的execte()方法替换submit()方法,可以进而继承ThreadPoolExecutor来扩展出自己的TreaceThreadPoolExecutor。

Guava中扩展的线程池:

  • MoreExecutors.directExecutor() :将任务在调用线程中执行。
  • 将普通线程池转为Daemon线程池。
  • 对Future模式扩展。

估算线程池大小

threadsSize=cpu数量*cpu使用率*(1+等待时间/计算时间)

Fork/Join线程池

分而治之的思想,例如MapReduce,将大任务划分为小任务合成为最终结果。

ForkJoinPool线程池接受提交ForkJoinTask,ForkJoinTask对象可以调用fork()来分叉出小任务。

 

 

并发

基础概念

并发与并行:并发是串行交替执行,并行就是并行执行。并行只能出现在多核CPU情况下了。但它们的执行结果可能是一致的。并发概念独立于程序语言,多数语言都会涉及到并发问题。 临界区:多线程争用的共享数据。 死锁、饥饿、活锁:死锁是两方都争用;饥饿是一方争用而另一方谦让;活锁是两方都谦让。

并发级别

阻塞无饥饿:公平锁,不论线程优先级别,线程按照顺序排队。 无障碍:非阻塞调度是一种乐观的策略。都无障碍的执行,一旦检测到冲突,立即回滚。 无锁:保证必然有一个线程在有限步内完成。 无等待:要求所有线程都必须在有限步内完成。

锁机制

锁机制有两个层面:一种是代码层次上的,另外一种是数据库层次上的。锁同步更多指的是应用程序的层面,多个线程进来,只能一个一个的访问同一个数据。

数据库锁机制

  • 悲观锁(Pessimistic Locking):独占锁,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制,也只有数据库层提供的锁机制才能真正保证数据访问的排他性。select * from account where name=”Erica” for update 这条 sql 语句锁定了 account 表中所有符合检索条件( name=”Erica” )的记录。

锁优化

  • 减小持有时间
  • 减小粒度:分割数据结构,适用于局部信息调用频繁,全局信息调用不频繁。
  • 锁粗化:把一组连续请求释放锁整合。
  • 读写分离锁:分割功能点,也是减小粒度的一种情况。
  • 锁分离:操作于结构上的位置不同,如take和put操作于LinkedBlockingQueue中链表的首尾。

JVM锁优化

  • 锁偏向:同一线程连续再次申请锁时,无须同步操作。适用于锁竞争少的场景。可以使用虚拟机参数开启锁偏向机制。
  • 轻量级锁:
  • 重量级锁:
  • 自旋锁:
  • 锁消除:通过逃逸分析,去除在没有竞争时使用的锁。可以使用虚拟机参数开启锁消除。