- 浏览: 392974 次
- 性别:
- 来自: 杭州
博客专栏
-
Spring技术内幕读书笔...
浏览量:15414
文章分类
最新评论
-
albert0707:
非常感谢!
JAVA NIO 实例 -
greatwqs:
表露清晰, 言简意赅, 重新温习了一次
MySQL事务隔离级别详解 -
lgh1992314:
新版本都不需要了。ServiceLoader<Drive ...
DriverManager和Class.forName()的异同 -
zipmoon:
像以上的实验,就没有出现数据幻读的问题。 ---原因:My ...
MySQL事务隔离级别详解 -
jnjeC:
贴代码来了。大家还要根据代码做一定的修改。########## ...
MySQL事务隔离级别详解
线程池就像数据库连接池一样,是一个对象池。所有的线程对象都有一个共同的目的,那就是为了提高对象的使用率,从而达到提高程序效率的目的。比如对于Servlet,它被设计为多线程的(如果它是单线程的,你就可以想象,当1000个人同时请求一个页面时,在第一个人获得请求结果之前,其它999个人都在郁闷地等待),如果为每个用户的每一次请求都创建一个新的线程对象来运行的话,系统就会在创建线程和销毁线程上耗费很大的开销,大大降低系统的效率。因此,Servlet多线程机制背后有一个线程池在支持,线程池在初始化初期就创建了一定数量的线程对象,通过提高对这些对象的利用率,避免高频率地创建对象,从而达到提高程序的效率的目的。
在本文中将实现一个最简单的线程池,从中理解它的实现原理。为此我们定义了四个类,它们的用途及具体实现如下:
Task(任务)
/** * 这是代表任务的抽象类,定义了一个deal方法,继承Task抽象类的子类 * 需要实现这个方法,并把这个任务需要完成的具体工作在deal方法中实现。 * 线程池中的线程之所以被创建,就是为了执行各种各样的任务,为了方便线程对任务的处理, * 需要用一个Task抽象类来保证任务的具体工作同一放在deal方法中来完成。 */ public abstract class Task { public enum State{ /*新建*/NEW, /*运行*/RUNNING, /*结束*/FINISHED } /* * 任务状态 */ private State state=State.NEW; public State getState() { return state; } public void setState(State state) { this.state = state; } public abstract void deal(); }
TaskQueue(任务队列)
/** * 任务队列,在同一时刻,可能有很多任务需要执行,而程序在同一时刻只能执行一定数量的任务, * 当需要执行的任务数超过了程序所能承受的任务数后怎么办,这就有了先执行那些任务,后执行 * 那些任务的规则。TaskQueue类就是这些规则中的一种,采用了FIFO(先进先出),也就是按照 * 任务到达的先后顺序执行。 */ public class TaskQueue { private List<Task> queue=new LinkedList<Task>(); //添加一项任务 public synchronized void addTask(Task task){ if(task!=null){ queue.add(task); } } //完成任务后将它从任务队列中删除 public synchronized void finishTask(Task task){ if(task!=null){ task.setState(Task.State.FINISHED); queue.remove(task); } } //取得一项待执行任务 public synchronized Task getTask(){ Iterator<Task> iterator=queue.iterator(); Task task; while(iterator.hasNext()){ task=iterator.next(); //寻找一个新建的任务 if(Task.State.NEW.equals(task.getState())){ //把任务状态置为运行中 task.setState(Task.State.RUNNING); return task; } } return null; } }
TaskThread(执行任务的线程)
/* * TaskThread是线程池中默认的线程处理程序, * 是执行任务的线程,专门用于执行任务队列中的待执行任务 * 不断地从任务队列中取出任务,然后执行 */ public class TaskThread extends Thread { //该线程所属的线程池 private ThreadPoolService service; public TaskThread(ThreadPoolService tps){ this.service=tps; } public void run(){ //在线程池运行的状态下执行 任务队列中的任务 while(service.isRunning()){ TaskQueue queue=service.getTaskQueue(); Task task=queue.getTask(); if(task!=null){ task.deal(); } queue.finishTask(task); } } }
ThreadPoolService(线程池服务类)
import java.util.ArrayList; import java.util.List; /** * 在创建的时候就创建了几个线程对象,但是线程并没有启动运行。 * 在调用了start方法启动线程池服务之后,它们才真正运行。 * stop方法可以停止线程池服务,同时通知池中所有线程的运行。 * runTask(Task task)将一个新的待执行任务交与线程池来运行。 */ public class ThreadPoolService { public enum Status{ NEW, RUNNING, TERMINATED } //线程数 public static final int THREAD_COUNT=5; //线程池状态 private Status status=Status.NEW; private TaskQueue queue=new TaskQueue(); private List<Thread> threads=new ArrayList<Thread>(); public ThreadPoolService(){ for(int i=0;i<THREAD_COUNT;i++){ Thread t=new TaskThread(this); threads.add(t); } } //启动服务 public void start(){ this.status=Status.RUNNING; for(int i=0;i<THREAD_COUNT;i++){ threads.get(i).start(); } } //停止服务 public void stop(){ this.status=Status.TERMINATED; } //是否在运行 public boolean isRunning(){ return status==Status.RUNNING; } //执行任务 public void runTask(Task task){ queue.addTask(task); } protected TaskQueue getTaskQueue(){ return queue; } }
完成了上面四个类,我们就实现了一个简单的线程池。现在我们就可以使用它了,下面的代码做了一个简单的示例。
class SimpleTask extends Task { private int i; public SimpleTask(int i) { super(); this.i = i; } @Override public void deal() { System.out.println(Thread.currentThread().getName()+"-----"+"我是SimpleTask : " + i); } } public class ThreadPoolTest { public static void main(String[] args) throws InterruptedException { ThreadPoolService service=new ThreadPoolService(); service.start(); for(int i=0;i<20;i++){ service.runTask(new SimpleTask(i)); } Thread.sleep(5000); service.stop(); } }
当然,我们实现的是最简单的,这里只是为了演示线程池的实现原理。在实际应用中,根据情况的不同,可以做很多优化。比如:
调整任务队列的规则,给任务设置优先级,级别高的任务优先执行。
动态维护线程池,当待执行任务数量较多时,增加线程的数量,加快任务的执行速度;当任务较少时,回收一部分长期闲置的线程,减少对系统资源的消耗。
事实上JAVA5.0及以上版本已经为我们提供了线程池功能,无需再重新实现。这些类位于java.util.concurrent包中。
Executors类提供了一组创建线程池对象的方法,常用的有以下几个:
Executors.newCachedThreadPool(); Executors.newFixedThreadPool(int nThreads); Executors.newSingleThreadExecutor();
newCachedThreadPool方法创建一个动态的线程池,其中线程的数量会根据实际需要来创建和回收,适合于执行大量短期任务的情况;newFixedThreadPool(int nThreads)方法创建一个包含固定数量线程对象的线程池,nThreads代表要创建的线程数,如果某个线程在运行的过程中因为异常而终止了,那么一个新的线程会被创建和启动来代替它;而newSingleThreadExecutor()方法则只在线程池中创建一个线程,来执行所有的任务
这三个方法都返回了一个ExecutorService类型的对象。实际上,ExecutorService是一个接口,它的submit()方法负责接收任务并交与线程池中的线程去运行。submit()方法能够接收Callable和Runnable两种类型的对象。它们的用法和区别如下:
1、Runnable接口:继承Runnable接口的类要实现它的run()方法,并将执行任务的代码放入其中,run()方法没有返回值。适合于只作某种操作,不关心运行结果的情况。
2、Callable接口:继承Callable接口的类要实现它的call()方法,并将执行任务的代码放入其中,call()将任务的执行结果作为返回值。适合于执行某种操作之后,需要知道执行结果的情况。
无论是接收Runnable型参数,还是接收Callable型参数的submit()方法,都会返回一个Future(也是一个接口)类型的对象。该对象中包含了任务的执行情况以及结果。调用Future的boolean isDone方法可以知道任务是否执行完毕;调用get()方法可以获得任务执行后的返回结果,如果此时任务还没有执行完,get()方法会保持等待,直到相应的任务执行完毕后,才会将结果返回。
我们用一个例子来演示JAVA5.0中的线程池的使用:
public class ExecutorTest { /** * @param args * @throws ExecutionException * @throws InterruptedException */ public static void main(String[] args) throws InterruptedException, ExecutionException { // TODO Auto-generated method stub ExecutorService es=Executors.newCachedThreadPool(); //提交任务 Future fr=es.submit(new RunnableTest()); //提交任务 Future fc=es.submit(new CallableTest()); //取得返回值并输出 System.out.println(fc.get()); //检查任务是否执行完毕 if(fr.isDone()){ System.out.println("执行完毕-RunnableTest.run()"); }else{ System.out.println("未执行完毕-RunnableTest.run()"); } //检查任务是否执行完毕 if(fc.isDone()){ System.out.println("执行完毕-CallableTest.run()"); }else{ System.out.println("未执行完毕-CallableTest.run()"); } //停止线程池服务 es.shutdown(); } } class RunnableTest implements Runnable{ public void run() { System.out.println("已经执行-RunnableTest.run()"); } } class CallableTest implements Callable{ public Object call() throws Exception { // TODO Auto-generated method stub System.out.println("已经执行-CallableTest.call()"); return "返回值-CallableTest.call()"; } }
运行结果:
已经执行-RunnableTest.run()
已经执行-CallableTest.call()
返回值-CallableTest.call()
执行完毕-RunnableTest.run()
执行完毕-CallableTest.run()
使用完线程池之后,需要调用它的shutdown()方法停止服务,否则其中的所有线程都会保持运行,程序不会退出。
评论
发表评论
-
Java动态绑定机制的内幕(转载)
2011-05-19 22:45 2275在Java方法调用的过程中,JVM是如何知道调用的是哪个类的方 ... -
Static和Final的深度理解
2011-05-19 10:57 4077在Java中,static和final是使用频率非 ... -
自己动手写DataSource
2010-11-02 13:09 6271DataSource 对象所表示的物理数据源 ... -
DriverManager和Class.forName()的异同
2010-10-30 12:48 6533在学习JDBC的时候,通常有两种方式去注册数据库驱动程序(这里 ... -
Java的局部内部类以及final类型的参数和变量
2010-09-27 15:52 4695Thinking In Java里 ... -
JAVA NIO 实例
2010-09-18 03:32 45982最近一直在忙着JAVA NIO的知识,花了一下午 ... -
重载与覆盖区别
2010-09-16 23:40 3702重载与覆盖区别 有时候,类的同一种功能有多种实现方式, ... -
HashCode和equals方法深度分析
2010-08-31 22:51 1477在往HashSet集合中放数据的时 ... -
[转]Java编码浅析
2010-08-29 16:09 1584Java与Unicode: Java的cl ... -
java final变量(转)
2010-08-26 14:47 2273final变量定义: 变量一经初始化就不能指向其它 ... -
JAVA中方法和变量在继承中的覆盖和隐藏
2010-08-24 13:07 1991关键字: java继承 方法覆盖 方法隐藏 ... -
java静态方法能否被重写
2010-08-24 11:52 13904JAVA静态方法 ... -
StringBuffer可变String不可变的真正原因
2010-08-23 19:19 2737String和StringBuffer 都是fi ... -
ANT笔记(二)
2010-06-09 09:01 2086四、 生成一个简单的 JA ... -
ANT笔记(一)
2010-06-09 08:59 1427一、 Ant ... -
Java的反射机制和动态代理
2010-06-09 08:29 1966运行时信息(RunT ... -
JNDI和Java RMI远程调用(二)
2010-06-07 21:22 5442利用 JNDI 定位资源 JNDI ... -
JNDI和Java RMI远程调用(一)
2010-06-07 11:32 5040对于 Java EE 开发来说, ... -
Java方法继承、方法重载、方法覆盖小总结
2010-03-17 14:59 1556Java方法继承、方法重载 ...
相关推荐
2.然后根据提示运行java命令执行示例程序,观看线程池的运行结果 目标:Java中多线程技术是一个难点,但是也是一个核心技术。因为Java本身就是一个多线程语言。本人目前在给46班讲授Swing的网络编程--使用Swing来...
1. 初始化线程执行提交的任务 2. 任务执行不过来,放入工作队列 3. 任务过多,线程和队列均处理不过来,拒绝执行(本文中将抛出RejectedExecuti
此文档是: 基于简单线程池概念的JAVA服务器端应用 附有连接ORACLE数据库等简单操作. 操作描述: 服务器启动后,会启动10个子线程运行.(配合客户端10个请求进行模拟,控制台输出模拟过程) 服务器主程序进入一个有...
主要介绍了Java线程池的应用,结合具体实例形式分析了java线程池的斐波那契数列计算与模拟工人做工等应用的操作技巧,需要的朋友可以参考下
2.然后根据提示运行java命令执行示例程序,观看线程池的运行结果 目标:Java中多线程技术是一个难点,但是也是一个核心技术。因为Java本身就是一个多线程语言。本人目前在给46班讲授Swing的网络编程--使用Swing来...
下面小编就为大家带来一篇java线程池:获取运行线程数并控制线程启动速度的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
Scheduled-executor:一个简单的golang库,用于模拟Java中著名的调度线程池执行程序
实例132 执行任务(线程池) 378 实例133 碰撞的球(多线程) 382 实例134 钟表(多线程) 387 实例135 模拟生产者与消费者 392 实例136 仿迅雷下载文件 396 第15章 图形编程 403 实例137 多变的按钮 403 ...
模拟停车场停车自写一份用到队列或者用线程池和Semaphore 完成 仅限参考
第1篇 Java编程基础 第1章 Java开发环境的搭建(教学视频:9分钟) 2 1.1 理解Java 2 1.2 搭建Java所需环境 3 1.2.1 下载JDK 3 1.2.2 安装JDK 4 1.2.3 配置环境 5 1.2.4 测试JDK配置...
基于redis,实现乐观锁、悲观锁实现秒杀系统,同时采用线程池的方式模拟实现用户抢购的请求
实例242 手术任务(线程池) 462 实例243 模拟人工服务台(线程连接池) 466 13.6 线程应用实例 471 实例244 下雪的村庄 472 实例245 小飞侠 474 实例246 飞流直下 477 实例247 多线程断点续传 479 实例248 滚动的...
实例214 模拟操作系统的进程调度 379 实例215 利用栈将字符串逆序输出 381 实例216 动态的数组链表 382 实例217 你能猜出鱼是谁的宠物吗? 387 实例218 使用Collections类对List的排序操作 393 ...
其中采用Java 的ExecutorService来进行线程池的方式实现多线程,模拟客户端多用户向同一服务器端发送请求. 注意,此为学习笔记,可以作为参考学习使用,不建议商业使用或生产使用。 废话不多说,直接上代码。
实例242 手术任务(线程池) 462 实例243 模拟人工服务台(线程连接池) 466 13.6 线程应用实例 471 实例244 下雪的村庄 472 实例245 小飞侠 474 实例246 飞流直下 477 实例247 多线程断点续传 479 实例248 ...
java代码雨源码Rain是一个基于统计的工作负载生成工具包,它使用参数化和经验分布来模拟不同类别的工作负载变化。 特征 高度灵活和可定制的工作负载特性 支持负载量的变化、操作的混合和数据流行度的变化(数据热点...
实例242 手术任务(线程池) 462 实例243 模拟人工服务台(线程连接池) 466 13.6 线程应用实例 471 实例244 下雪的村庄 472 实例245 小飞侠 474 实例246 飞流直下 477 实例247 多线程断点续传 479 实例248 滚动的...
1.运用线程池原理,支持大量并发连接,命令处理速度快。 2.可以主动批量下发Deliver消息,可以自定义Deliver消息的任何字段内容,以及MO消息发送的循环次数,相邻两条消息的间隔时间。 3.具有日志记录功能。 4....