线程池

现在服务器端的应用程序几乎都采用了“线程池”技术,这主要是为了提高系统效率。因为如果服务器对应每一个请求就创建一个线程的话,在很短的一段时间内就会产生很多创建和销毁线程动作,导致服务器在创建和销毁线程上花费的时间和消耗的系统资源要比花在处理实际的用户请求的时间和资源更多;线程池就是为了尽量减少这种情况的发生。

下面我们来看看怎么用Java实现一个线程池。一个比较简单的线程池至少应包含线程池管理器、工作线程、任务队列、任务接口等部分。其中线程池管理器(ThreadPool Manager)的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务时进行等待;任务队列的作用是提供一种缓冲机制,将没有处理的任务放在任务队列中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。


1.为什么要使用线程池
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,服务器应用程序需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务,这就是“池化资源”技术产生的原因。
线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
2.线程池的组成部分
一个比较简单的线程池至少应包含线程池管理器、工作线程、任务列队、任务接口等部分。其中线程池管理器的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务是进行等待;任务列队的作用是提供一种缓冲机制,将没有处理的任务放在任务列队中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。
线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务。
工作线程是一个可以循环执行任务的线程,在没有任务时将等待。
任务接口是为所有任务提供统一的接口,以便工作线程处理。任务接口主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等。
3.线程池适合应用的场合
当一个服务器接受到大量短小线程的请求时,使用线程池技术是非常合适的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率。但是线程要求的运动时间比较长,即线程的运行时间比

[img]http://dl2.iteye.com/upload/attachment/0103/2684/31f497f8-a103-3410-99ef-5a09e5e6b54d.png[/img]




package com.jzk.lession9;

import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
* ThreadPool Test
*
* @see ================================================================================================
* @see 线程与进程的区别
* @see 1)多个进程的内部数据和状态是完全独立的,而多线程则会共享一块内存空间和一组系统资源,有可能互相影响
* @see 2)线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈。所以线程的切换比进程切换的负担要小
* @see ================================================================================================
* @see 线程的启动方式和消亡
* @see 1)以Thread.start()启动时,JVM会以线程的方式运行它。start()首先会为线程的执行准备系统资源,然后才去调用run方法
* @see 2)以Thread.run()启动时,JVM会以普通方法运行它。此时就不会存在线程所特有的交替执行的效果
* @see 3)一个线程类的两个对象,同时以start()方式运行时,JVM仍会把它们当作两个线程类来执行
* @see 4)终止线程时,绝对不能使用stop()方法,而应该让run()自然结束
* @see ================================================================================================
* @author jinzk
* @create Feb 29, 2014 11:13:43 AM
*/
public class ThreadPoolTest {

public static void main(String[] args) {

// threadPoolTest();
// threadPoolScheduledTest();
//threadPoolCallbaleAndFutureSignTest();
threadPoolCallbaleAndFutureMoreTest();
}

/**
* 创建线程池的几种方式
*
* @see Executors.newFixedThreadPool(3); //创建固定大小的线程池
* @see Executors.newCachedThreadPool(); //创建缓存线程池。它会根据实际请求的线程数量动态创建线程
* @see Executors.newSingleThreadExecutor(); //创建单个线程池。它可以实现线程死掉后重新启动的效果,但实际启动的是"替补线程"
*/
public static void threadPoolTest() {

// newSingleThreadExecutor()的好处就是,若池中的线程死了,它会把一个"替补的线程"扶上位,即它会保证池中始终有一个线程存在
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for (int i = 1; i <= 10; i++) {
final int task = i;
threadPool.execute(new MyTask(task));
}
System.out.println("all of 10 tasks has committed......");
// 线程池中的任务均执行完毕后,关闭线程池
threadPool.shutdown();
System.out.println("threadPool has shutdown......");
}

/**
* 线程池启动定时器
*
* @see Executors.newScheduledThreadPool(3).schedule(); //创建并执行在给定延迟后启用的一次性操作
* @see Executors.newScheduledThreadPool(3).scheduleAtFixedRate(); //首次启动后,以固定的频率自动执行操作
* @see scheduleAtFixedRate()支持间隔重复任务的定时方式,但不直接支持绝对定时方式,我们需要转换为相对时间的方式,来执行
*/
public static void threadPoolScheduledTest() {

// 10秒后自动执行一次
// Executors.newScheduledThreadPool(3).schedule(new MyScheduledTask(), 10, TimeUnit.SECONDS);

// 6秒后首次执行,之后每2秒均自动执行一次
Executors.newScheduledThreadPool(3).scheduleAtFixedRate(new MyScheduledTask(), 6, 2, TimeUnit.SECONDS);
}

/**
* 线程池返回一个任务的值
*
* @see 注意:这里需使用java.util.concurrent.ExecutorService.submit()来提交,它会返回Future对象
* @see 注意:Future取得的结果类型,与Callable返回的结果类型,必须一致。我们这里是通过泛型来实现的
*/
public static void threadPoolCallbaleAndFutureSignTest() {

ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<String> future = threadPool.submit(new Callable<String>() {

@Override
public String call() throws Exception {

Thread.sleep(2000);
return "张起灵";
}
});
System.out.println("等待结果");
try {
System.out.println("拿到结果:" + future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
threadPool.shutdown();
}

/**
* 线程池返回多个任务的值
*
* @see java.util.concurrent.CompletionService用于提交一组Callable任务
* @see CompletionService.take()会返回已完成的一个Callable任务所对应的Future对象
* @see 这就好比同时种植了几块菜地,然后等待收菜。收菜时,哪块菜熟了,就先去收哪块菜地的菜
*/
public static void threadPoolCallbaleAndFutureMoreTest() {

ExecutorService threadPool = Executors.newFixedThreadPool(5);
CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPool);
for(int i=1; i<=5; i++){

final int taskCode = i;
completionService.submit(new Callable<Integer>() {

@Override
public Integer call() throws Exception {

int time = new Random().nextInt(5000);
Thread.sleep(time);
System.out.println("taskCode: " + taskCode + " has sleeped " + time);
return taskCode;
}
});
}

for(int i=1; i<=5; i++){
try {
System.out.println(completionService.take().get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
threadPool.shutdown();
}

static class MyTask implements Runnable {

private int task = 0;

public MyTask(int task) {

this.task = task;
}

@Override
public void run() {

for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " is looping of " + i + " for task of " + task);
}
}

}

static class MyScheduledTask implements Runnable {

@Override
public void run() {

System.out.println("bombing......");
}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值