关于Lab6中利用线程池技术

当任务A与任务B执行顺序之间有逻辑关系时(例如先执行A,再执行B),单单使用多线程并发技术并不能确保这种逻辑关系的能够被实现,因为即使任务A线程优先于任务B线程首先被创建,但是由于时间片切换机制,可能任务A线程在创建之后就被挂起不执行,而任务B线程创建之初就开始执行,这样就造成程序运行结果不如预期,而且这种逻辑错误难以通过debug技术中的单步运行发现。例如下面代码,就不满足先创建先执行的逻辑。

  1. public class Snippet  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.         for (int i = 1; i <= 20; i++)  
  6.         {  
  7.             new Thread(new test(i)).start();  
  8.         }  
  9.     }  
  10. }  
  11.   
  12. class test implements Runnable  
  13. {  
  14.     /** 记录线程创建时间*/  
  15.     private int time;  
  16.       
  17.     public test(int time)  
  18.     {  
  19.         this.time = time;  
  20.     }  
  21.       
  22.     @Override  
  23.     public void run()  
  24.     {  
  25.         System.out.print(time+" ");  
  26.     }  
  27. }  

 

  1. 执行结果:3 2 1 4 6 5 7 8 10 11 9 12 13 14 16 15 17 18 20 19   

可以看出有很多线程先被创建后于后被创建的线程执行。

 

利用线程池技术可以改变线程之间逻辑执行顺序问题,使任务A执行完成之后再开启任务B。

 

从应用线程池的角度,简单介绍一下线程池

 

一、创建线程池:

        四种类型:

  1. newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

 

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  

 

  1. newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  

 

 

  1. newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

 

  1. newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

 

  1. 如果不想使用上述现成的线程池,也可以自己设定参数

ThreadPoolExecutor executor = new ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue);  

                 参数解释:

  1. corePoolSize:核心池的大小。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;

 

  1. maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;

 

  1. keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

 

  1. unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性

TimeUnit.DAYS;               //天

TimeUnit.HOURS;             //小时

TimeUnit.MINUTES;           //分钟

TimeUnit.SECONDS;           //秒

TimeUnit.MILLISECONDS;      //毫秒

TimeUnit.MICROSECONDS;      //微妙

TimeUnit.NANOSECONDS;       //纳秒

 

  1. workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

ArrayBlockingQueue;

LinkedBlockingQueue;

SynchronousQueue;

 

二、往线程池中加入任务

executor.execute(thread);  

                通过这个方法可以向线程池提交一个任务,交由线程池去执行。

 

三、关闭线程池

executor.shutdown();  

调用了shutdown()方法,线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕

 

四、等待线程池里的任务执行完毕

while (!executor.isTerminated());  

 

通过上述方法,可以实现任务A执行完成之后再开启任务B的逻辑。

  1. public class Snippet  
  2. {  
  3.     public static void main(String[] args)  
  4.     {  
  5.         ExecutorService executor;  
  6.         for (int i = 1; i <= 20; i++)  
  7.         {  
  8.             executor = Executors.newCachedThreadPool();  
  9.             executor.execute(new test(i));  
  10.             executor.shutdown();  
  11.             while (!executor.isTerminated())  
  12.             {  
  13.                   
  14.             }  
  15.         }  
  16.     }  
  17. }  
  18.   
  19. class test implements Runnable  
  20. {  
  21.     /** 记录线程创建时间*/  
  22.     private int time;  
  23.     public test(int time)  
  24.     {  
  25.         this.time = time;  
  26.     }  
  27.     @Override  
  28.     public void run()  
  29.     {  
  30.         System.out.print(time+" ");  
  31.     }  
  32. }  
  33.   
  34. 运行结果:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20   

可以看出现在程序执行就是按照先创建的先执行的逻辑顺序执行

 

在本次实验线程之间执行顺序也有类似的逻辑关系,例如:在第n秒,所有未过河的猴子需要根据决策采取行动之后,才能开启第n+1秒的猴子进程。这就需要用线程池技术管理猴子线程,使猴子线程满足时间上先后执行顺序的关系。

在本次实验中思路如下:

Step1:判断猴子是否全部过河完毕,如果是,结束程序,如果不是执行step2

        Step2:新建线程池,为每一只未过河的猴子创建一个线程,加入到线程池

        Step3:关闭线程池,等待线程执行完毕

        Step4:时间自增1,重复上述操作

注:本人实现本次实验思路是以秒为单位,在第i秒里都会为每一只未过河的猴子创建线程(在每一个线程里,未过河的猴子只会执行一步操作),然后加入到线程池,利用线程池管理这些线程,等待这些线程全部执行完毕,然后销毁,才进入到i+1秒的状态。到下一秒后,继续为未过河猴子创建线程,重复上述操作,知道所有猴子过河。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值