在多线程程序中,主要是围绕着任务的执行来展开的,所谓的任务是指一些抽象的且离散的工作单元,通过把应用程序的工作分解到多个任务中,每个任务之间有一定的事物边界,各个任务可以同时执行,从而以并发的形式来提高执行效率
编写并发程序的时候,首要的是找到清晰的任务边界,各个任务之间应该是相互独立的。独立性有助于并发,只有有足够的资源(计算资源以及存储资源)的情况下,这些任务是可以并行执行的。例如在web服务器中,可以以独立的客户请求作为任务的边界,为不同的请求建立不同的线程来进行响应。
应用程序可以通过多种策略来调度任务的执行:
1.串行的执行任务。
在单个线程中穿行执行用户的请求,这种做法最为简单,但是其效率不如人意。
ServerSocket socket = new ServerSocket(80);
while(true){
Socket connection = socket.accept();
handleRequest(connection);
}
2.显示的为任务创建线程
为每个请求创建一个单独的线程进行处理。
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();
}
上面的列子将请求的处理放在单独的线程中进行执行,从而可以再主线程中更快的接受其他用户的请求,从而提高了程序的吞吐量,因为会有多个线程调用handleRequest(Socket)方法,所以必须保证该方法的线程安全性。
该方式的缺点:
1。线程的生命周期开销是非常高的,每个请求创建一个线程,请求处理完之后释放线程,线程的请求和释放都会消耗大量的资源,如果在请求的到达率非常高而请求处理的过程却是轻量级的时候,大部分的资源都被消耗在创建于销毁线程上面。
2.资源的消耗,活跃的线程会消耗系统的资源,尤其是内存如果可运行线程的数量大大多于可用处理器的数量,那么有些线程就会被闲置,占用大量的内存,如果现有的线程已经保持了处理器的繁忙,那么再添加过多的线程不会增加性能,反而会降低性能,因为这些线程竞争处理器资源的时候会消耗更多的性能。
3.线程池
维护一定数量的线程,当任务到达的时候,在池中取一个空闲的线程,运行该任务,如果该任务正常的完成,那么任务完成之后该线程被返回线程池中等待下一个任务的到来,这种方式避免了创建线程以及销毁线程的开销。
java类库中对线程池的实现提供了实现,在java.util.concurrent包中提供了Executor框架,该框架中用Executor接口代替Thread对任务的执行进行抽象。
public interface Executor{
void execute(Runnable command);
}
可以通过Executors中的静态工厂方法之一来创建一个线程池
1.newFixedThreadPool:该方法将创建一个固定大小的线程池,初始化大小为0,之后每接受一个任务就新建一个线程知道到达最大的线程数目,此时线程的数量不在变化。
2.newCachedThreadPool:如果线程的规模超过了需求的时候,那么回收空闲的线程,当需求增加的时候新建新的线程,线程的最大数目没有限制。
3.newSingleThreadExecutor:创建单个线程线性执行任务
4.newScheduledThreadPool:创建一个固定长度的线程池,以延迟或者定时的方式来执行任务。
Executor的生命周期:
public interface ExecutorService extends Executor{
//shutdown()方法平缓的关闭线程池,不在接受新的任务,等待已经执行或者等待执行的任务执行完毕
void shutdown();
//采用粗暴的关闭方法,试图关闭所有运行中的任务,且等待队列中的任务不在执行。
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
//接收一个Callable集合,接收一个一一对应的Future集合
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit) throws InterruptedException;
}