Java线程——线程池ExecutorService

诸如Web服务器、数据库服务器、文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务。请求以某种方式到达服务器,这种方式可能是通过网
络协议(例如HTTP、FTP或POP)、JMS队列或者轮询数据库。不管请求如何到达,在服务器应用程序中经常出现的情况是:单个任务处理的时间很短而请求的数目却是巨大的。

构建服务器应用程序的一个过于简单的模型应该是:当一个请求到达就创建一个新线程,然后在新线程中为请求服务。实际上对于原型开发这种方法工作得很好,但如果试图部署以这种方式运行的服务器应用程序,那么这种方法的严重不足就很明显了。每个请求对应·个线程(thread一per一request)方法的不足之一是:为每个请求创建一个新线程的开销很大。为每个请求创建新线程的服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多。

除了创建和销毁线程的开销之外,活动的线程也消耗系统资源。在一个JVM里创建太多的线程可能会导致系统由于过度消耗内存而用完内存或“切换过度”。为了防止资源不足,服务器应用程序需要一些办法来限制任何给定时刻处理的请求数目。

线程池为线程生命周期开销问题和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。其好处是,因为在请求到达时线程已经存在,所以无意中也消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使应用程序响应更快。而且,通过适当地调整线程池中的线程数目,也就是当请求的数目超过某个阀值时,就强制其他任何新到的请求一直等待,直到获得一个线程来处理为止,从而可以防止资源不足。

线程池是管理线程的高级技术,通常它提供了如下几个功能。
通过对线程的管理,更加合理的调配资源。通常,线程池里维护着一组空闲线程,并向外提供,根据系统繁忙程度动态增加或减少空闲线程的数量。比较高级的还提供了自动检测异常线程的功能。

通过维护池中即存线程,可以节省创建线程的开销,尤其是对于Web Server这类处理频繁而处理过程又比较快的程序,创建线程的开销是不能忽略的。

java.util.concurrcnt提供了包括互斥、信号量、诸如在并发访问下执行得很好的队列和散列表之类的集合类及几个工作队列实现。该包中的Executors类是一种有效的、广泛使用的以工作队列为基础的线程池的正确实现。你无须尝试编写你自己的线程池,这样做容易出错。

创建线程池的方法很简单,只需要使用Executors.newFixedThreadPool(i)函数即可创建大小为i的线程池,实例属于ExecutorService类型。然后可以调用该类的execute()函数来启动一个线程,输入的参数即为线程变量。

下面是一个简单的例子,使用了2个大小的线程池来处理100个线程。需要注意的是,线程池必须使用shutdown来显式关闭,否则主线程就无法退出。shutdown也不会阻塞主线程。
完整源码如下

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestExecutors {
    public void main(String[] args){
        ExecutorService exec= Executors.newFixedThreadPool(2);
        for(int index=0;index<100;index++){
            Runnable run=new Runnable(){
                public void run(){
long time= (long) (Math.random()*1000);
System.out.println("休眠"+time+"ms");
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            exec.execute(run);
        }
        exec.shutdown();

    }
}

运行结果:

休眠888ms
休眠466ms
休眠246ms
休眠961ms
休眠587ms
休眠249ms
休眠413ms
休眠370ms
休眠253ms
休眠447ms
...........

但有一个问题:在for循环的过程中,会等待线程池有空闲的线程,所以主线程会阻塞。为了解决这个问题,一般启动一个线程来做for循环,就是为了避免由于线程池满了造成主线程阻塞。如下所示:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestThreadFor extends Thread {
@Override
    public void run() {
        ExecutorService exec= Executors.newFixedThreadPool(2);
        for(int index=0;index<100;index++){
            Runnable run=new Runnable(){
                public void run(){
                    long time= (long) (Math.random()*1000);
                    System.out.println("休眠"+time+"ms");
                    try {
                        Thread.sleep(time);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            exec.execute(run);
        }
        exec.shutdown();
    }
    public static void main(String[] args){
        new TestThreadFor().start();
    }
}

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