1.多线程套接字编程中的不足
记得之前做过学校的一个实验,是关于多线程套接字编程地,在那次实验中,我们采用了如下的代码:
ServerSocket server = new ServerSocket(80);
while(true){
//新建线程
new Thread(xxx).start;
}
在上方代码,我们为了提升服务器的效率,我们使用了多线程编程,但是同时,这个代码,他也是会无限制创建线程地,他的不足主要有:
1.线程每次创建和销毁的代价非常高;2.对内存的消耗很大,可运行的线程数量很可能多于处理器数量;3.稳定性,如果线程超过了当前虚拟机或者操作系统的限制,那很可能抛出异常。
针对这种代码,我们给出了更好的解决方案,就是使用线程池。
2.1关于Executor框架和线程池
Excetor是一个接口,它里面就是一个execute方法,参数是Runnable类型,作用就是执行这个实现了Runable接口的类的线程。很简单,但是为灵活且强大的线程池(异步执行框架)提供了基础。
关于线程池的使用:
类A {
private static final Exceutor exex = Executors.newFixedThreadPool(100);
public static void main(String[] args)throws IOException{
ServerSocket socket = new ServerSocket(80);
Runnable task = new B//某个实现了Runnable的类
exec.execute(task);
}
}
关于线程池的优势:1.可以宠用现有线程,而不用创建新线程,节省开销;2.可以限定线程池大小,创建足够多的线程让系统保持忙碌,却不会耗尽资源使任务失败。
类库提供了一些Executors静态工厂方法来创建一个线程池:
1.newFixedThreadPool。 创建一个固定长度的线程池
2.newCachedThreadPool。 创建一个可缓存的线程池,如果线程池的当前规模超过了处理需求时,那么将回收空闲的线程,而当需求增加时则可以添加新的线程,线程池的规模不存在任何限制。
3.newSingleThreadExecutor。这是一个单线程的线程池,他创建单个工作者线程来执行任务,如果这个线程异常结束,会创建另一个线程来代替。
4.newScheduledThreadPool。创建了一个固定长度的现车公尺,而且一延时或定时的方式来执行任务,类似Timer定时器。
newFixedThreadPool和newCachedThreadPool这两个工厂方法返回通用的ThreadPoolExecutor实例,可以直接用来构造专门用途的exxecutor
2.2线程池的生命周期
ExecutorService 拓展了Executor接口,新增了关于结束线程池生命的方法,就是说,我们可以用ExecutorService接口,就可以承接真是的线程池实例类了。
主要方法有shutdown()//正常、安全、缓慢关闭; 还有shutdownnow//强行快速关闭线程池
2.3线程池和携带结果的任务,Callable和Future
Runnable是一种有局限的抽象,run,它并不能返回一个值。所以我们有了Callable接口,他认为主入口点(call方法)将返回一个值,并可能抛出一个异常。
Future表示一个任务的生命周期,并提供方法来判断是否完成或者取消,获取任务结果,他隐含了一个含义,任务的生命周期只能前进,不能后退。他也是一个接口。
get方法取决于任务状态,如果任务还没完成就会阻塞。
关于Future和Callable的使用例子,使用Future等待图像下载:
class FutureRenderer {
private final ExecutorService executor = .;
void renderPage(CharSequence source) {
final List<ImageInfo> imageInfos = scanForImageInfo(source);
Callable<List<ImageData>> task = new Callable<List<ImageData>> () {
public List<ImageData> call() {
List<ImageData> result = new ArrayList<ImageData>();
for(ImageInfo imageInfo : imageInfos) {
result.add(imageInfo.downloadImage());
}
return result;
}
};
Future<List<ImageData>> future = executor.submit(task);
renderText(source);
try{
List<ImageData> imageData = future.get();
for(ImageData data : imageData) {
rederImage(data);
}
} catch (InterruptedException e) {
//重新设置线程的中断状态
Thread.currentThread().interrupt();
//由于不需要结束,因此取消任务
future.cancel(true);
} catch (ExecutionException e) {
throw launderThrowable(e.getCause());
}
}
}
我们还可以通过Future的get方法来为任务设置时间限制:
V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计算超时,将抛出TimeoutException