- 线程池
一种最为简单的线程创建和回收的方法类:
new Thread(new Runable() {
public void run() { //do sth}
}).start();
在 run() 方法结束后,自动回收改线程。
在简单的应用系统中,这段代码并没有太多问题。但是在真实生产环境中,系统由于真实环境需要,
可能会开启很多线程来支撑其应用。
在实际生产环境中,线程的数量必须得到控制。盲目的大量创建线程对于系统性能是很有伤害的。
JDK 对线程池的支持
JDK 提供了一套 Executor 框架,有效进行线程控制。
ThreadPoolExecutor 表示一个线程池。
Executors 扮演线程池工厂的角色,Executors 可以取得一个拥有特定功能的线程池。Executor 框架提供了各种类型的线程池:
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()
public static ScheduleExecutorService newSingleThreadScheduledExecutor()
public static ScheduleExecutorService newScheduledThreadPool(int corePoolSize)
1) newFiexedThreadPool() :返回一个固定线程数的线程池。线程池中线程数量始终不变。
当新任务提交时,线程池有空闲线程,就立即执行。若没有,会被存在一个任务队列中,有空闲时,便处理任务队中的任务。
2) newSingleTHreadExecutor() :返回一个只有一个线程的线程池。
若多余一个任务被提交,任务会被保存在一个任务队中,待线程空闲,按先入先出的顺序执行队列中的任务。
3) newCachedThreadPool() :返回一个可根据实际情况调整线程数量的线程池。
线程池数量不确定,若有空闲线程可以复用,则优先使用。若所有线程均在工作,又有新任务提交,则会创建新的线程处理。所有线程在当前任务执行完毕后,将返回线程池进行复用。
4) newSingleThreadScheduledExecutor() 返回一个 ScheduledExecutorService 对象,线程池大小为1.
ScheduledExecutorService 接口在 ExecutorService 接口之上扩展了在给定时间执行某任务的功能。
如在某个固定的延时之后执行,或者周期性执行某个任务。
5) new ScheduledThreadPool() :返回一个 ScheduledExecutorService 对象。
但改线程池可以指定线程数量。
//固定线程池
public class ThreadPoolDemo {
public static class MyTask implements Runnable {
@Override
public void run() {
System.out.println(System.currentTimeMillis() + ":Thread ID:" + Thread.currentThread().getId());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyTask task = new MyTask();
ExecutorService es = Executors.newFixedThreadPool(5);
for (int i = 0;i < 10;i++) {
es.submit(task);
}
}
}
输出:
1483410660734:Thread ID:12
1483410660734:Thread ID:15
1483410660734:Thread ID:14
1483410660734:Thread ID:13
1483410660734:Thread ID:11
1483410661737:Thread ID:12
1483410661737:Thread ID:15
1483410661737:Thread ID:14
1483410661737:Thread ID:11
1483410661737:Thread ID:13
- 计划任务
ScheduledExectorServic 对象,主要方法:
public ScheduledFuture<?> schedule(Runable command,long delay,TimeUnit unit)
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,
long period,TimeUnit unit)
public ScheduleFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,
long delay TimeUnit unit)
scheduleAtFixedRate() 和 scheduleWithFixedDelay() 会对任务进行周期性调度。
对于 fixedRate, 任务调度的频率是一定的。它是以上一个任务开始执行时间为起点,
之后的 period 时间,调度下一次任务。
FixDelay,上一个任务结束之后,再经过 delay时间进行任务调度。
扩展线程池
ThreadPoolExecutor 提供了 beforeExecute()、afterExecute() 和 terminated() 三个接口扩展优化线程池线程数量
< java concurrency in practice> 一书中给出估算线程池大小的经验公式:
Ncpu = CUP 的数量
Ucpu = 目标CPU的使用率,0 <= Ucpu <= 1
W / C = 等待时间与计算时间的比率为保持处理器达到期望的使用率,最优的池的大小等于:
Nthreads = Ncpu * Ucpu * (1 + W/C )
在 java 中,可以通过:
Runtime.getRuntime().availableProcessors()
取得可用的CPU数量。
在线程池中寻找堆栈
一种最简单的方法,放弃 submit(),改用 execute().
pools.execute(new DivTask(100,i));或者使用下面改造: Future re = pools.submit(new DivTask(100,i)); re.get();
并发集合简介
1) concurrentHashMap: 高效的并发 HashMap. 线程安全的 HashMap.
2) CopyOnWriteArrayList: 是一个 List,在读多写少的场合,这个 list 的性能非常好,远远好于 Vector
3) concurrentLinkedQueue: 高效的并发队列,使用链表实现。线程安全的 LinkedList。
4) BlockingQueue: 接口,JDK内部通过链表、数组等方式实现这个接口。
表示阻塞队列,非常适合用于作为数据共享的通道。
5) concurrentSkipListMap: 跳表的实现。是一个Map,使用跳表的数据结构进行快速查找。java.util 下的 Vector 是线程安全的,Collections 可以帮助我们将任意集合包装成线程安全的集合。