关于线程池
java中线程的创建、销毁、线程之间的切换是一件十分耗费计算机资源的事情。如果我们需要使用多线程处理任务,并频繁的创建,销毁线程会造成计算机资源的无端浪费,甚至会导致系统资源的崩溃。因此,我们真正在项目中,使用的是线程池技术。
线程池技术
1.线程池的好处:
1).降低系统资源的消耗,通过重用已经存在的线程,降低线程创建和销毁造成的损耗。
2).提高响应速度。当任务达到时,任务可以不需要等到线程创建就能立即执行。
3).提高线程的可管控性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
4).提供更强大的功能,比如:延时定时线程池。
2.工作原理:
在线程池的编程模式下,任务是提交给整个线程池,而不是直接提 交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务
线程池工作的本质就是将要执行的任务添加到队列中,然后线 程池寻求空闲的线程来执行队列里面的任务。3.线程池结构
4.线程池里线程的几种状态的转换:
1、线程池在构造前(new操作)是初始状态,一旦构造完成线程池就进入了执行状态RUNNING。严格意义上讲线程池构造完成后并没有线程被立即启动,只有进行“预启动”或者接收到任务的时候才会启动线程。
2、线程池运行中可以通过shutdown()和shutdownNow()来改变运行状态。线程池Executor是异步的执行任务,因此任何时刻不能够直接获取提交的任务的状态。这些任务有可能已经完成,也有可能正在执行或者还在排队等待执行。
shutdown()是一个平缓的关闭过程,线程池停止接受新的任务,同时等待已经提交的任务执行完毕,包括那些进入队列还没有开始的任务,这时候线程池处于SHUTDOWN状态;shutdownNow()是一个立即关闭过程,线程池停止接受新的任务,同时线程池取消所有执行的任务和已经进入队列但是还没有执行的任务,这时候线程池处于STOP状态。
3、一般情况下我们认为shutdown()或者shutdownNow()执行完毕,线程池就进入TERMINATED状态,此时线程池就结束了。
当然,在shutdown/stop到TERMINATED状态之间还存在一个TIDYING状态。
创建线程池
java中使用 ThreadPoolExecutor来表示线程池,创建该类对象来 表示创建一个线程池
/*
* corePoolSize:核心线程数,在没有用的时候,也不会被回收
* maximumPoolSize:最大线程数
* keepAliveTime:线程池中除了核心线程之外,其它线程最长可以保存时间
* unit:时间单位
* workQueue:等待队列,任务可以储存在任务队列中等待被执行
*/
ThreadPoolExecutor executor =
new ThreadPoolExecutor(2, 6, 2, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
使用线程池
public class ThreadPoolTest {
public static void main(String[] args) {
/*
* corePoolSize:核心线程数
* maximumPoolSize:最大线程数
* keepAliveTime:线程池中除了核心线程之外,其它线程最长可以保存时间
* unit:时间单位
* workQueue:等待队列,任务可以储存在任务队列中等待被执行
*/
ThreadPoolExecutor executor =
new ThreadPoolExecutor(2, 6, 2, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
//提交任务去执行
Runnable t1 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
Runnable t2 = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
Runnable t3 = new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
};
//任务执行完毕,Jvm并不会关闭,因为里面的核心线程没有结束掉
executor.execute(t1);
executor.execute(t2);
executor.execute(t3);
//关闭线程池 等到里面所有的线程空闲了才会关闭
//如果有线程正在执行,那就等待 执行完毕之后再关闭
executor.shutdown();
}
}
关于几种BlockingQueue<Runnable> 实现队列的选择:
1. ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。 ArrayBlockingQueue。静态工厂方Executors.newFixedThreadPool()使用了这个队列。
2. LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于
3. SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
4. PriorityBlockingQueue:一个具有优先级得无限阻塞队列。
几种常用的线程池
newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程 会在队列中等待
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执 行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
四种线程池,使用方式大同小异,这里我就不一一阐述了,其中NewScheduledThreadPool 增加了延迟执行和周期性,这里我给出了NewSchedualedThreadPool线程池的普通用法:
public class NewSchedualedThreadPoolTest {
public static void main(String[] args) {
//还增加了延迟执行和周期性
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
// executorService.execute(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName());
// }
// });
//延迟执行
// executorService.schedule(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName());
// }
// }, 3, TimeUnit.SECONDS);
//延迟1s执行,以后每隔2s执行一次
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}, 1, 2, TimeUnit.SECONDS);
}
}