线程池的概念与Executors类的应用
创建固定大小的线程池
创建缓存线程池
创建单一线程池
关闭线程池
shutdown与shutdownNow的比较
用线程池启动定时器
调用ScheduledExecutorService的schedule方法,返回的ScheduleFuture对象可以取消任务。
支持间隔重复任务的定时方式,不直接支持绝对定时方式,需要转换成相对时间方式。
关于线程池的讲解:
首先介绍在Tcp服务器编程模型的原理,每一个客户端连接用一个单独的线程为之服务,当与客户端的会话结束时,线程也就结束了,即每来一个客户端连接,服务器端就要创建一个新线程。这好比假设每个报名学员都要通过我来亲自接待,以便给每个学员一种好的感觉,但每个学员报名手续要花费半个小时,对于50名同学,我一个个接待和为之办理手续,显然不实际,我会怎么做呢?我会先接待每一个学员,打完招呼后,再把他分配给一名工作人员去办理手续,这样,我就接待了每名学员。
如果访问服务器的客户端很多,那么服务器要不断地创建和销毁线程,这将严重影响服务器的性能。如果真的来一名学员,我们都安排一名新工作人员为之服务,也是不可能的,那公司岂不是要招聘很多工作人员?而是应该一名工作人员服务完一名学员,空闲下来后,一旦有新的学员要服务,我又立即安排该工作人员为新学员服务。线程池的概念与此类似,首先创建一些线程,它们的集合称为线程池,当服务器接受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。
在线程池的编程模式下,任务是提交给整个线程池,而不是直接交给某个线程,线程池在拿到任务后,它就在内部找有无空闲的线程,再把任务交给内部某个空闲的线程,这就是封装。记住,任务是提交给整个线程池,一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
固定大小的线程池&缓存线程池-----------------:
步骤1:用3个大小的固定线程池去执行10个内部循环10次就结束的任务,为了观察固定线程池下的其他任务一直再等待,希望打印出正在执行的线程名、任务序号和任务内部的循环次数,刚开始看到只有3个线程在执行,并看到任务前仆后继的效果。注意:这10个任务要用各自独立的runnable对象,才能看到任务的序号。
步骤2:改为缓存线程池,可以看到当前有多少个任务,就会分配多少个线程为之服务。
package cn.itcast.foundationsummary;
importjava.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
importjava.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
publicstatic void main(String[] args) {
//ExecutorServiceservice = Executors.newFixedThreadPool(3);
ExecutorServiceservice = Executors.newCachedThreadPool();
for(inti=1;i<=10;i++){
finalint sequence = i;
//仔细品味runnable对象放到循环里面和外面的区别,为了让每个对象有自己独立的编号
service.execute(newRunnable(){
publicvoid run() {
try{Thread.sleep(200);}catch(Exceptione){}
for(intj=1;j<=5;j++){
System.out.println(Thread.currentThread().getName()+ "is serving "
+sequence + " task:" + "loop of " + j);
}
}
});
}
/*
用下面这句代码来说明上面的代码是在提交任务,并且所有的任务都已经提交了,但任务是什么时候执行的,则是由线程池调度的!
*/
System.out.println(“all task have committed!”);
//注意与service.shutdownNow()的区别。
service.shutdown();
ScheduledExecutorServicescheduledService = Executors.newScheduledThreadPool(1);
scheduledService.scheduleAtFixedRate(
newRunnable(){
publicvoid run() {
System.out.println("bomb!!!");
}},
5,
1,
TimeUnit.SECONDS);
}
}
/
如果没有线程池,需要在run方法中不停判断,还有没有任务需要执行
线程池的通俗比喻:接待客户,为每个客户都安排一个工作人员,接待完成后该工作人员就废掉。服务器每收到一个客户请求就为其分配一个线程提供服务,服务结束后销毁线程,不断创建、销毁线程,影响性能。
线程池:先创建多个线程放在线程池中,当有任务需要执行时,从线程池中找一个空闲线程执行任务,任务完成后,并不销毁线程,而是返回线程池,等待新的任务安排。
线程池编程中,任务是提交给整个线程池的,并不是提交给某个具体的线程,而是由线程池从中挑选一个空闲线程来运行任务。一个线程同时只能执行一个任务,可以同时向一个线程池提交多个任务。
线程池创建方法:
a、创建一个拥有固定线程数的线程池
ExecutorServicethreadPool = Executors.newFixedThreadPool(3);
b、创建一个缓存线程池 线程池中的线程数根据任务多少自动增删 动态变化
ExecutorServicethreadPool = Executors.newCacheThreadPool();
c、创建一个只有一个线程的线程池 与单线程一样 但好处是保证池子里有一个线程,
当线程意外死亡,会自动产生一个替补线程,始终有一个线程存活
ExecutorServicethreadPool = Executors.newSingleThreadExector();
往线程池中添加任务
threadPool.executor(Runnable)
关闭线程池:
threadPool.shutdown() 线程全部空闲,没有任务就关闭线程池
threadPool.shutdownNow() 不管任务有没有做完,都关掉
用线程池启动定时器:
a、创建调度线程池,提交任务 延迟指定时间后执行任务
Executors.newScheduledThreadPool(线程数).schedule(Runnable, 延迟时间,时间单位);
b、创建调度线程池,提交任务, 延迟指定时间执行任务后,间隔指定时间循环执行
Executors.newScheduledThreadPool(线程数).schedule(Runnable, 延迟时间,
间隔时间,时间单位);
所有的 schedule 方法都接受相对延迟和周期作为参数,而不是绝对的时间或日期。将以 Date
所表示的绝对时间转换成要求的形式很容易。例如,要安排在某个以后的Date运行,可以使用:schedule(task, date.getTime()- System.currentTimeMillis(), TimeUnit.MILLISECONDS)。
public class ThreadPoolTest {
/**
* @param args
*/
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newFixedThreadPool(3);
//ExecutorService threadPool = Executors.newCachedThreadPool();
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for(int i=1;i<=10;i++){
final int task = i;
threadPool.execute(new Runnable(){
@Override
public void run() {
for(int j=1;j<=10;j++){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is looping of " + j + " for task of " + task);
}
}
});
}
System.out.println("all of 10 tasks have committed! ");
//threadPool.shutdownNow();
Executors.newScheduledThreadPool(3).scheduleAtFixedRate(
new Runnable(){
@Override
public void run() {
System.out.println("bombing!");
}},
6,
2,
TimeUnit.SECONDS);
}
}