zoukankan      html  css  js  c++  java
  • Java多线程(四) 线程池

      一个优秀的软件不会随意的创建、销毁线程,因为创建和销毁线程需要耗费大量的CPU时间以及需要和内存做出大量的交互。因此JDK5提出了使用线程池,让程序员把更多的精力放在业务逻辑上面,弱化对线程的开闭管理。

      JDK提供了四种不同的线程池给程序员使用  

      首先使用线程池,需要用到ExecutorService接口,该接口有个抽象类AbstractExecutorService对其进行了实现,ThreadPoolExecutor进一步对抽象类进行了实现。最后JDK封装了一个Executor类对ThreadPoolExecutor进行实例化,因此通过Executor能够创建出具有如下四种特性的线程池

      1. 无下界线程池

            ExecutorService threadPool= Executors.newCachedThreadPool( ); 当线程数不足时,线程池会动态增加线程进行后续的任务调度

      2. 固定线程数的线程池

             ExecutorService threadPool= Executors.newFixedThreadPool(3); 创建具有固定线程数:3个的线程池

      3. 单线程线程池

       ExecutorService threadPool= Executors.newSingleThreadScheduledExecutor( );创建单例线程池,确保线程池内恒定有1条线程在工作

      4. 调度线程池

         ScheduledExecutorService threadPool= Executors.newSingleThreadScheduledExecutor( ); 创建一个定长线程池,定时或周期性地执行任务

     1 package com.scl.thread.threadPool;
     2 
     3 import java.util.concurrent.Executors;
     4 import java.util.concurrent.ScheduledExecutorService;
     5 import java.util.concurrent.TimeUnit;
     6 
     7 public class ScheduledThreadPool
     8 {
     9     public static void main(String[] args)
    10     {
    11         // 创建定时调度线程池
    12         ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
    13         // 定时执行Runnable
    14         threadPool.scheduleAtFixedRate(new Runnable()
    15         {
    16 
    17             @Override
    18             public void run()
    19             {
    20                 System.out.println("do some big Scheduled");
    21             }
    22         }, 10, 3, TimeUnit.SECONDS);
    23     }
    24 }
    定时调度线程池

            使用线程池也比较简单,只要往线程池里面提交"任务"即可,一般使用对象.submit的方法进行,如下:

       ① threadPool.submit(Callable<T>task);    

                   Callable跟Runnable接口一样在使用接口时会自动调用里面的call方法。与Runnable接口不一样的地方在于run方法不能返回内容,call方法含有返回参数,在外部可以通过Future 接口来接受线程返回的结果。如:Future<String> future = threadPool.submit(new MyTask( ));

             ② threadPool.submit(Runnable task);

                由这个签名可以看出,同样地可以往线程池里面扔入Runnable接口实例。

      这如上面所说,线程池的使用就是减少程序员对线程的启动和关闭。把线程的创建及关闭交给线程池来完成。因此使用线程池来完成线程的经典问题:生产者-消费者模型。

      模型简介:该模型是一个典型的排队内容。模型分为三个主要角色:生产者、消费者、仓库。生产者和消费者共享仓库信息,①当仓库里面的产品满了,停止生产等待消费者去消费到足够的量再进行生产  ②当仓库里面的产品为空,等待生产者生产后再进行销售  ③仓库负责承载产品内容

      根据模型内容,做成了一个车位管理的例子。分为三个角色:持车人(CarOwner)、保安(Secure)、车库(CarPark)。不同的持车人把车驶入到车库,保安负责记录车辆离开车库的数量。车库容量固定,如果车库为空保安可以通知持车人把车驶入车库。如果车库满了,保安告知持车人等待另外的持车人把车驾驶出来。车库使用一个列表对入库的车信息进行记录。

      因此有如下类图:

                               

     1 package com.scl.thread.threadPool.CarParkManager;
     2 
     3 public class CarOwner implements Runnable
     4 {
     5     private int driverInNum;
     6     private CarPark carPark;
     7 
     8     public CarOwner(CarPark carPark)
     9     {
    10         this.carPark = carPark;
    11     }
    12 
    13     @Override
    14     public void run()
    15     {
    16         carPark.driverIn(driverInNum);
    17     }
    18 
    19     public int getDriverInNum()
    20     {
    21         return driverInNum;
    22     }
    23 
    24     public void setDriverInNum(int driverInNum)
    25     {
    26         this.driverInNum = driverInNum;
    27     }
    28 }
    持车人 CarOwner
     1 package com.scl.thread.threadPool.CarParkManager;
     2 
     3 import java.util.ArrayList;
     4 import java.util.List;
     5 import java.util.UUID;
     6 
     7 public class CarPark
     8 {
     9     protected int MaxCarNum;
    10     private List<Car> carList = new ArrayList<Car>();
    11 
    12     public CarPark(int maxNum)
    13     {
    14         this.MaxCarNum = maxNum;
    15     }
    16 
    17     public void driverIn(int num)
    18     {
    19         synchronized (carList)
    20         {
    21             while (num + carList.size() > MaxCarNum)
    22             {
    23                 System.out.println(Thread.currentThread() + ":无法进库,目前车库数目:" + carList.size());
    24                 try
    25                 {
    26                     carList.wait();
    27                 }
    28                 catch (InterruptedException e)
    29                 {
    30                     e.printStackTrace();
    31                 }
    32             }
    33             for (int i = 0; i < num; i++)
    34             {
    35                 try
    36                 {
    37                     Thread.sleep(20);
    38                 }
    39                 catch (InterruptedException e)
    40                 {
    41                     e.printStackTrace();
    42                 }
    43                 String uuid = UUID.randomUUID().toString();
    44                 Car c = new Car(uuid, uuid + i);
    45                 carList.add(c);
    46             }
    47             System.out.println(Thread.currentThread() + ":已经驶入" + num + "辆,目前车库数目:" + carList.size());
    48             carList.notify();
    49         }
    50     }
    51 
    52     public void driverOut(int num)
    53     {
    54         synchronized (carList)
    55         {
    56 
    57             while (carList.size() < num)
    58             {
    59                 System.out.println(Thread.currentThread() + ":无法驶出" + num + "辆,目前车库数目:" + carList.size());
    60                 try
    61                 {
    62                     carList.wait();
    63                 }
    64                 catch (InterruptedException e)
    65                 {
    66                     e.printStackTrace();
    67                 }
    68             }
    69             for (int i = num - 1; i >= 0; i--)
    70             {
    71                 try
    72                 {
    73                     Thread.sleep(20);
    74                 }
    75                 catch (InterruptedException e)
    76                 {
    77                     e.printStackTrace();
    78                 }
    79                 carList.remove(i);
    80             }
    81             System.out.println(Thread.currentThread() + ":已经驶出" + num + "辆,目前车库数目:" + carList.size());
    82             carList.notify();
    83         }
    84     }
    85 
    86     public List<Car> getCarList()
    87     {
    88         return carList;
    89     }
    90 
    91     public void setCarList(List<Car> carList)
    92     {
    93         this.carList = carList;
    94     }
    95 
    96 }
    停车场 CarPark
     1 package com.scl.thread.threadPool.CarParkManager;
     2 
     3 public class Car
     4 {
     5     private String id;
     6     private String name;
     7 
     8     public Car(String id, String name)
     9     {
    10         this.id = id;
    11         this.name = name;
    12     }
    13 
    14 }
    汽车信息类 Car
     1 package com.scl.thread.threadPool.CarParkManager;
     2 
     3 public class Secure implements Runnable
     4 {
     5     private int driverOutNum;
     6 
     7     private CarPark carPark;
     8 
     9     public Secure(CarPark carPark)
    10     {
    11         this.carPark = carPark;
    12     }
    13 
    14     public int getDriverOutNum()
    15     {
    16         return driverOutNum;
    17     }
    18 
    19     public void setDriverOutNum(int driverOutNum)
    20     {
    21         this.driverOutNum = driverOutNum;
    22     }
    23 
    24     @Override
    25     public void run()
    26     {
    27         carPark.driverOut(driverOutNum);
    28     }
    29 }
    保安 Secute
     1 package com.scl.thread.threadPool.CarParkManager;
     2 
     3 import java.util.concurrent.ExecutorService;
     4 import java.util.concurrent.Executors;
     5 
     6 public class Client
     7 {
     8 
     9     public static void main(String[] args)
    10     {
    11         // 设置车库最大值
    12         CarPark carPark = new CarPark(5);
    13         // 创建线程池
    14         ExecutorService threadPool = Executors.newCachedThreadPool();
    15         // 创建三个持车人
    16         CarOwner cw1 = new CarOwner(carPark);
    17         CarOwner cw2 = new CarOwner(carPark);
    18         CarOwner cw3 = new CarOwner(carPark);
    19         // 设置需要驶入的车辆数目
    20         cw1.setDriverInNum(3);
    21         cw2.setDriverInNum(3);
    22         cw3.setDriverInNum(1);
    23 
    24         // 创建三个保安
    25         Secure s1 = new Secure(carPark);
    26         Secure s2 = new Secure(carPark);
    27         Secure s3 = new Secure(carPark);
    28         s1.setDriverOutNum(1);
    29         s2.setDriverOutNum(2);
    30         s3.setDriverOutNum(1);
    31 
    32         threadPool.submit(cw1);
    33         threadPool.submit(cw2);
    34         threadPool.submit(cw3);
    35         threadPool.submit(s1);
    36         threadPool.submit(s2);
    37         threadPool.submit(s3);
    38 
    39     }
    40 
    41 }
    客户端 Client

     补充:很多时候我们都会写一些demo,然后用JUnit进行测试。多线程用JUnit来测试真的很好用,但是有个很不人性化的就是:线程池里面的任务都作为一个类似C#所说的工作者线程进行调用。即:当主线程运行完毕,线程池里面的东西还没执行完毕整个程序就退出了。让主线程去等待子线程执行,有几个方法:①用子线程调用join方法(线程池不适用,本来线程池就是要弱化对线程的操作)②在主线程内用Thread.sleep(10000); 然而我们没法知道线程池内的任务到底要多久,sleep的数值一直要设置很大 ③找个方法获取线程池是否把任务执行完毕。毫无疑问第三种方法是最佳的。

      获取任务是否完成,需要按照以下两个步骤执行。(假如有线程池threadPool)

      1. 停止往线程池提交任务。调用threadPool.shutdown( ); 

      2. 循环判断线程池是否空置。调用threadPool.isTerminated( );

          即可在Junit测试方法内,在添加完所有任务后,加上如下代码:

     1         threadPool.shutdown();
     2         while (true)
     3         {
     4             //如果任务已经执行完毕,则跳出循环
     5             if (threadPool.isTerminated())
     6             {
     7                 break;
     8             }
     9             //主线程等待
    10             Thread.sleep(1000);
    11         }
    12         //彻底关闭线程池
    13         threadPool.shutdownNow();

    由上述代码可见,使用线程池和使用Thread类进行提交没有太大的差异。JDK5提供了一个阻塞队列的,能够更好地模拟生产者-消费者模型。后续再进行总结。

    以上为线程池的总结内容,如有错漏烦请指出纠正。

  • 相关阅读:
    [07] Redis 持久化
    [06] Redis 事务
    [05] Jedis
    [04] Redis 配置文件
    [03] Redis 数据类型
    [02] Redis 简介&安装
    [01] NoSQL 简介
    06-NULL&typedef
    05-动态内存分配
    朴素贝叶斯分类器Naive Bayes
  • 原文地址:https://www.cnblogs.com/doucheyard/p/5752297.html
Copyright © 2011-2022 走看看