多线程设计模式——Thread Pool(线程池)模式

这些都是根据我最近看的《Java实战指南多线程编程(设计模式篇)》所得整理。

模式名称

Thread Pool(线程池)模式

模式解决的问题

一个系统中的线程相对与其所要处理的任务而言,总有一种非常有限的资源,线程不仅在其执行任务是需要消耗CPU时间和内存等资源,线程对象本身以及线程所需的调用栈也占用内存,并且Java中创建一个线程往往意味着JVM会创建相应的依赖与宿主机操作系统的本地线程。所以,为每个或者每一批任务创建一个线程以对其进行执行,通常是一种奢侈而不现实的事情。

解决思路

比较常用的做法就是服用一定数量的线程,由这些线程去执行不断产生的任务。
ThreadPool类负责接收和存储任务以及工作者线程的生命管理。submit用于接收一个任务,客户端代码调用该方法想线程池提交一个任务;shutdown关闭线程池对外提供的服务
Promise可借以获取相应任务执行结果的凭据对象。setResule获取相应任务执行结果的执行结果,getResule设置相应任务执行结果的执行结果。
WorkQueue工作队列,实现任务的缓存。enqueue将任务存入队列,dequeue从队列中取出一个任务。
WorkerThread负责任务执行的工作这线程。run逐一从工作队列中取出任务执行,runTask执行制定的任务。

Created with Raphaël 2.1.0 client client threadPool threadPool workQueue workQueue promist promist 1submit() 2enqueue() 3 4creat 5

例子代码

某系统在用户执行一些关键的操作前要求其输入一个验证码,验证码是一串随机数字,由该系统的服务器端代码生成并通过短信发送给用户。

public class SmsVerficationCodeSender {
    private static final ExecutorService EXECUTOR = new ThreadPoolExecutor(1,
            Runtime.getRuntime().availableProcessors(),60,TimeUnit.SECONDS,
            new SynchronousQueue(),new ThreadFactory(){
                @Override
                public Thread newThread(Runnable r){
                    thread t = new Thread(r,"VerfCodeSender");
                    t.setDaemon(true);
                    return t;
                }
    });

    //生成并下发验证码短信到制定的手机号码
    public void sendVerificationSme(final String misisdn){
        Runnable task = new Runnable(){
            @Override
            public void run(){
                //生成强随机数验证码
                int verificationCode = ThreadSpeciticSecureRandom.INSANCE
                    .nextInt(999999);
                DecimalFormat df = new DecimalFormat("000000");
                String txtVerCode = df.format(verificationCode);

                //发送验证码信息
                sendSms(misisdn,txtVerCode);
            }
        };

        EXECUTOR.submit(task);
    }

    private void sendSms(String msisdn,String verificationCode){
        System.out.println("Sending verification code " + verificationCode + " to "
                + msisdn);

        //忽略其他与设计模式无关的代码
    }
}

模式的评价与考量

ThreadPool模式通过服用一定数量的工作者线程去执行不断被提交的任务,节约了线程这种有限二昂贵的资源。该模式还有以下几个好处:

  1. 抵消线程创建的开销,提高响应性
  2. 封装了工作者线程生命周期管理
  3. 减少销毁线程的开销

尽管该模式有这些好处,它仍然有一些风险和问题

  1. 工作队列的选择:通常有三种队列方式,有界队列(BoundedQueue)工作队列本身并不限制线程池中等待运行的任务的数量,但工作队列中实际可容纳的任务取决于任务本身对资源的使用情况;无界队列(UnboundQueue)工作队列限定线程池中等待大人物的数量,在一定成都上可以限制资源的消耗;直接交接队列(SymchrinousQueue)不适用缓冲空间内部提交任务的时候调用的是工作队列的非阻塞式入队列方法,所以没有等待队列,会有新的线程对入队列失败的任务进行处理。
  2. 线程池大小调校:太大了浪费资源,太大无法充分利用资源,所以线程池大小取决于该线程池所要处理任务的特性,系统资源以及任务锁使用的稀缺资源状况。
  3. 线程池监控:线程池的大小,工作队列的容量,线程空闲时间限制这些熟悉的调试过程需要有程序去监控来方便调试ThreadPoolExecutor类提供了监控的方法。
  4. 线程泄露:线程池中的工作者线程会意外终止,使得线程池中实际可用的工作者线程减少。出现的原因是线程对象的run方法的异常处理没有捕获RuntimeException和Error导致run方法意外返回,使得相应线程意外终止。所以要注入捕获相应异常。但是还有一种可能情况需要注意,如果线程需要请求外部资源而且对外部资源的请求没有时间限制的话,线程实际上可能已经泄露了。
  5. 可靠性和线程池饱和处理策略:工作队列的选择对于线程大小需求变化没有处理方式,所以需要线程饱和处理策略。
  6. 死锁:线程请求类似的资源可能形成死锁。
  7. 线程池空闲线程清理:过长时间没有进行任务处理的线程是对系统资源的浪费,所以需要相应的处理代码。

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