java5线程并发库的应用(九)

线程池的概念与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);
}


}

你可能感兴趣的:(java线程)