1.Excutor框架出现的背景
在正常负载情况下,服务器应用程序应该兼具良好的吞吐量和快速的响应性。同时在过载时应平缓的劣化,而不应该负载一高就简单地以失败告终。为了达到这个目的,你要选择一个清晰的任务边界,并配合一个明确的任务执行策略。
1.1顺序化执行任务
看下面的例子:
class SingleThreadWebServer{
public static void main(String[] args){
ServerSocket socket = new ServerSocket(80);
while(true)
Socket connection = socket.accept();
handleRequest(connection);
}
}
}
上面的例子是实现了一个单线程化的Web Server,缺点很明显:只有当主线程处理完当前请求,才能继续接收其他请求。同时单线程化在等待I/O操作时,cpu基本处于闲置状态,因此导致了资源利用率非常低。
顺序化处理几乎不能为服务器应用程序提供良好的吞吐量和快速的用户响应,而且没有任何并发性而言。
1.2 显式为任务创建线程
class ThreadPerTaskWebServer{
public static void main(String[] args){
ServerSocket socket = new ServerSocket(80);
while(true)
final Socket connection = socket.accept();
Runnable task = new Runnable(){
public void run(){
handleRequest(connection);
}
}
new Thread(task).start();
}
}
}
上面的例子为每个socket分配了一个线程,这样主线程可以不断接受连接请求,从而提高响应性和并发性。在中等强度的负载水平下,每线程没任务方法对顺序化执行进行了良好的改进。只要请求的到达速度尚未打到服务器的处理能力,那么这种方法可以同时带来更快的响应性和更大的吞吐量。
1.3无限制创建线程的缺点:
1.线程生命周期的开销:线程的创建和销毁不是免费的,创建线程需要时间,会带来请求的延时,如果请求是大量而且频繁的,每线程每任务会消耗大量的计算资源。
2.内存消耗和cpu消耗:一个线程需要一定的内存,而且会给gc带来压力,同时大量线程会竞争cpu资源,还能产生其他的性能开销。
3.稳定性:每个线程会为栈分配一定的内存,如果打破了操作系统的限制,会收到一个OutOfMemory异常。
2.Excutor框架
无限制线程,有很多缺陷,而有界的线程可以防止资源过载而耗尽内存。作为Excutor的一部分,java.util.concurrent包提供了一个灵活的线程池实现。在java类库中,任务执行的首要抽象不是Thread,而是Excutor:
public interface Executor{
void execute(Runnable command);
}
package executor;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
*
*<p>Test</p>
*<p>Description:</P>
*<p>Company:Cisco CAS</p>
*<p>Department:CAS</p>
*@Author: Tommy Zhou
*@Since: 1.0
*@Version:Date:2011-5-7
*
**/
public class ExecutorTest {
public static void main(String[] args) {
/**
* Creates a thread pool that reuses a fixed number
* of threads operating off a shared unbounded queue.
* At any point, at most nThreads threads will be
* active processing tasks. If additional tasks
* are submitted when all threads are active,
* they will wait in the queue until a thread is
* available.
* If any thread terminates due to a failure during execution prior
* to shutdown, a new one will take its place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly shutdown.
*创建一个固定大小的线程池,如果当前线程数目已经超过线程池尺寸大小,
*当此时有新的请求任务,将被放置在队列里面等待,直到线程池有闲置线程为止。
*如果在线程执行过程中,遇到未知异常而终止,将补充一个新的线程
*/
Executor exec = Executors.newFixedThreadPool(20);
/**
* 创建一个可缓存的非固定大小的线程池,当有新的请求时,
* 如果池中有闲置的线程,而重用已有的闲置线程,如果没有
* 则新创建一个,线程的数量没有上限。并且那些没有在60秒之内使用的
* 线程会被终止并且从cache中移除,因此那些在池中闲置长时间的线程并不会
* 消耗任何资源。
* Threads that have not been used
* for sixty seconds are terminated and removed from the cache. Thus,
* a pool that remains idle for long enough will not consume any resources.
*
*
*/
Executor execCachedPool = Executors.newCachedThreadPool();
/**
* 创建一个单线程化的executor,它创建唯一的线程来执行任务,如果这个线程异常结束,会有
* 另外一个线程代替它
*
*/
Executor singleTaskPool = Executors.newSingleThreadExecutor();
/**
* 创建一个定长的线程池,而且支持定时或者周期性的任务执行
*/
ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(20);
// exec.equals(obj)
}
}