http://www.importnew.com/8542.html
http://liuzl121.iteye.com/blog/1779039
首先说下java实现多线程的两种方式,这个很多人应该都知道,简单的说说,一种是实现Runnable接口,另一种是继承Thread类,两个方法各有各的好处吧,实现Runnable接口的话可以做到多个线程之间的资源共享,同时java是单继承的,如果继承了Thread类就不能在继承其他类了,而实现了Runnable接口的话就可以同时继承其他类。而继承Thread类的优点呢是获取当前的线程信息比较简单,而我觉得继承Runnable接口的也不复杂,所有个人比较青睐用实现Runnable接口的这种方式。
再说下线程池的原理,线程池会让我联想起数据库连接池,数据库连接池的作用就是给你个数据库连接,然后我们用这个连接去执行操作,而不论我们执行什么操作,数据库连接都不会与我们的逻辑耦合,像一个工具类一样;同样线程池也要满足这样的要求,与我们的逻辑解耦。实现解耦的话就要用到‘多态’或‘接口回调’,就是通过调用父类或接口中的方法从而间接地调用我们所要执行的代码。
以上这些是思路吧,说说原理,不知道有没有人跟我一样,原来我一直以为线程池的话就是多个线程,某一个线程跑完了我们的逻辑方法就结束了,那既然结束了再怎么去接着执行别的任务,所以感觉多线程很难;其实多线程多线程就是通过线程调用线程实现的;打个比方来说就像“摆渡”,河的一岸有很多的人,他们想过河,过河的过程做什么就是他们自己的逻辑,只要他符合我的要求我就送你过河(线程池的要求就是实现Runnable或继承Thread类),然后我开了几条船去送人,只要河的这一岸有满足的人,我就送你过河。这个例子中河一岸的人就是我们要执行的任务,是一个集合,船就是线程池中的线程,由我们自己控制,过河这个动作就是要执行的逻辑,我们只负责把船调给你,怎么划桨怎么过河就是程序自己的逻辑了。
可能说了这么多还是不是很清楚(原谅我的表达能力!),看一个生产者和消费者的例子,生产者不停的生产产品,生产后放入仓库,消费者不停的从仓库里拿产品。我们用线程池将他们实现。
先是创建商品类,商品类中有商品信息:
- /**
- * 产品类
- */
- class Product {
- private String id;// 产品id
- private String name;// 产品名称
- public Product(String id, String name) {
- this.id = id;
- this.name = name;
- }
- @Override
- public String toString() {
- return "(产品ID:" + id + " 产品名称:" + name + ")";
- }
- public String getId() {
- return id;
- }
- public void setId(String id) {
- this.id = id;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
然后创建仓库类,仓库对外开放两个方法,存入和取出,由于是多线程,所以要用synchronized关键字修饰这两个方法。
- /**
- *仓库
- */
- class Storage {
- // 仓库容量为10
- private Product[] products = new Product[10];
- private int top = 0;
- // 生产者往仓库中放入产品
- public synchronized void push(Product product) {
- while (top == products.length) {
- try {
- System.out.println("仓库满了");
- wait();//仓库已满,等待
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- //把产品放入仓库
- products[top++] = product;
- System.out.println(Thread.currentThread().getName() + " 生产了产品"
- + product);
- notifyAll();//唤醒等待线程
- }
- // 消费者从仓库中取出产品
- public synchronized Product pop() {
- while (top == 0) {
- try {
- System.out.println("仓库空了");
- wait();//仓库空,等待
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- //从仓库中取产品
- --top;
- Product p = new Product(products[top].getId(), products[top].getName());
- products[top] = null;
- System.out.println(Thread.currentThread().getName() + " 消费了产品" + p);
- notifyAll();//唤醒等待线程
- return p;
- }
- }
然后生产者消费者的实例,实现Runnable接口,run方法中分别调用入库和出库的方法。
- /**
- * 消费者
- */
- class Consumer implements Runnable {
- private Storage storage;
- public Consumer(Storage storage) {
- this.storage = storage;
- }
- @Override
- public void run() {
- storage.pop();
- }
- }
- /**
- * 生产者
- */
- class Producer implements Runnable {
- private Storage storage;
- public Producer(Storage storage) {
- this.storage = storage;
- }
- @Override
- public void run() {
- Product product = new Product("090505105", "电话");
- storage.push(product);
- }
- }
之后就是关键的线程池的实现,线程池首先是一个容器,放着执行的线程和待执行的任务,线程池也只有一个所以用单例的设计模式。对外公开存放作业的方法和设置最大线程数的方法,当外界有作业放进来的时候就执行,执行完了之后销毁。上代码。
- package thread;
- import java.util.Collections;
- import java.util.Date;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.concurrent.Executor;
- import org.apache.log4j.Logger;
- /**
- * 线程池
- * 创建线程池,销毁线程池,添加新任务
- *
- * @author obullxl
- */
- public final class ThreadPool {
- /* 单例 */
- private static ThreadPool instance = ThreadPool.getInstance();
- /* 默认池中线程数 */
- private static int worker_num = 5;
- /* 等待任务队列 */
- private static List<Runnable> taskQueue = Collections
- .synchronizedList(new LinkedList<Runnable>());
- /* 池中的所有线程 */
- public PoolWorker[] workers;
- /**
- * 构造器
- * */
- private ThreadPool() {
- workers = new PoolWorker[5];
- for (int i = 0; i < workers.length; i++) {
- workers[i] = new PoolWorker(i);
- }
- }
- private ThreadPool(int pool_worker_num) {
- worker_num = pool_worker_num;
- workers = new PoolWorker[worker_num];
- for (int i = 0; i < workers.length; i++) {
- workers[i] = new PoolWorker(i);
- }
- }
- /**
- * 修改最大线程数
- * */
- public void setMaxThreadNum(int max){
- worker_num = max;
- }
- /**
- * 单例返回本对象
- * */
- public static synchronized ThreadPool getInstance() {
- if (instance == null)
- return new ThreadPool();
- return instance;
- }
- /**
- * 增加新的任务
- * @param newTask
- */
- public void addTask(Runnable newTask) {
- synchronized (taskQueue) {
- taskQueue.add(newTask);
- /* 开始执行 */
- this.startWork();
- }
- }
- /**
- * 批量增加新任务
- * @param taskes
- */
- public void batchAddTask(List<Runnable> taskes) {
- if (taskes == null || taskes.size() == 0) {
- return;
- }
- synchronized (taskQueue) {
- for (int i = 0; i < taskes.size(); i++) {
- if (taskes.get(i) == null) {
- continue;
- }
- taskQueue.add(taskes.get(i));
- }
- /* 唤醒队列, 开始执行 */
- // taskQueue.notifyAll();
- }
- for (int i = 0; i < taskes.size(); i++) {
- if (taskes.get(i) == null) {
- continue;
- }
- }
- /* 开始执行 */
- this.startWork();
- }
- /**
- * 执行作业
- * */
- private void startWork(){
- for (int i=0;i<workers.length;i++){
- if(!workers[i].isRunning){
- workers[i].start();
- }
- }
- }
- /**
- * 销毁线程池
- */
- public synchronized void destroy() {
- for (int i = 0; i < worker_num; i++) {
- workers[i].stopWorker();
- workers[i] = null;
- }
- taskQueue.clear();
- }
- /**
- * 池中工作线程
- *
- * @author obullxl
- */
- private class PoolWorker extends Thread {
- private int index = -1;
- /* 该工作线程是否有效 */
- private boolean isRunning = false;
- /* 该工作线程是否可以执行新任务 */
- private boolean isWaiting = true;
- public PoolWorker(int index) {
- this.index = index;
- }
- public void stopWorker() {
- this.isRunning = false;
- }
- public boolean isWaiting() {
- return this.isWaiting;
- }
- /**
- * 循环执行任务
- * 这也许是线程池的关键所在
- */
- public void run() {
- isRunning = true;
- while (isRunning) {
- Runnable r = null;
- synchronized (taskQueue) {
- if (taskQueue.isEmpty()) {
- stopWorker(); // (1) 没有作业就不执行
- // try { (2) 没有作业就等待,知道有作业加进来
- // /* 任务队列为空,则等待有新任务加入从而被唤醒 */
- // taskQueue.wait(20);
- // } catch (InterruptedException ie) {
- // logger.error(ie);
- // }
- }else{
- /* 取出任务执行 */
- r = taskQueue.remove(0);
- }
- }
- if (r != null) {
- r.run();
- }
- isWaiting = true;
- r = null;
- }
- }
- }
- }
然后写一个main方法模拟调用
- public class ProducersAndConsumers {
- public static void main(String[] args) {
- ThreadPool pool = ThreadPool.getInstance();
- Storage storage = new Storage();
- List<Runnable> conTasks = new ArrayList<Runnable>();
- for(int i=0;i<100;i++){
- Consumer con = new Consumer(storage);
- Producer pro = new Producer(storage);
- conTasks.add(con);
- conTasks.add(pro);
- }
- pool.batchAddTask(conTasks);
- }
- }
将线程池中待执行任务容器中放入任务,然后线程池开启5个线程,去调用我们待执行的任务,从而实现了同一时间内只有5个线程在工作。
演示结果:
- 仓库空了
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-0 消费了产品(产品ID:090505105 产品名称:电话)
- 仓库空了
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-0 消费了产品(产品ID:090505105 产品名称:电话)
- 仓库空了
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-2 消费了产品(产品ID:090505105 产品名称:电话)
- Thread-2 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-0 消费了产品(产品ID:090505105 产品名称:电话)
- Thread-3 消费了产品(产品ID:090505105 产品名称:电话)
- Thread-1 生产了产品(产品ID:090505105 产品名称:电话)
- Thread-4 消费了产品(产品ID:090505105 产品名称:电话)
线程池负责管理工作线程,包含一个等待执行的任务队列。线程池的任务队列是一个Runnable集合,工作线程负责从任务队列中取出并执行Runnable对象。
java.util.concurrent.executors 提供了 java.util.concurrent.executor 接口的一个Java实现,可以创建线程池。下面是一个简单示例:
首先创建一个Runable 类:
WorkerThread.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package
com.journaldev.threadpool; public
class WorkerThread implements
Runnable { private
String command; public
WorkerThread(String s){ this .command=s; } @Override public
void run() { System.out.println(Thread.currentThread().getName()+ " Start. Command = " +command); processCommand(); System.out.println(Thread.currentThread().getName()+ " End." ); } private
void processCommand() { try
{ Thread.sleep( 5000 ); }
catch (InterruptedException e) { e.printStackTrace(); } } @Override public
String toString(){ return
this .command; } } |
下面是一个测试程序,从 Executors 框架中创建固定大小的线程池:
SimpleThreadPool.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
package
com.journaldev.threadpool; import
java.util.concurrent.ExecutorService; import
java.util.concurrent.Executors; public
class SimpleThreadPool { public
static void
main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool( 5 ); for
( int
i = 0 ; i <
10 ; i++) { Runnable worker =
new WorkerThread( ""
+ i); executor.execute(worker); } executor.shutdown(); while
(!executor.isTerminated()) { } System.out.println( "Finished all threads" ); } } |
在上面的程序中,我们创建了包含5个工作线程的固定大小线程池。然后,我们向线程池提交10个任务。由于线程池的大小是5,因此首先会启动5个工作线程,其他任务将进行等待。一旦有任务结束,工作线程会从等待队列中挑选下一个任务并开始执行。
以上程序的输出结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
pool- 1 -thread- 2
Start. Command = 1 pool- 1 -thread- 4
Start. Command = 3 pool- 1 -thread- 1
Start. Command = 0 pool- 1 -thread- 3
Start. Command = 2 pool- 1 -thread- 5
Start. Command = 4 pool- 1 -thread- 4
End. pool- 1 -thread- 5
End. pool- 1 -thread- 1
End. pool- 1 -thread- 3
End. pool- 1 -thread- 3
Start. Command = 8 pool- 1 -thread- 2
End. pool- 1 -thread- 2
Start. Command = 9 pool- 1 -thread- 1
Start. Command = 7 pool- 1 -thread- 5
Start. Command = 6 pool- 1 -thread- 4
Start. Command = 5 pool- 1 -thread- 2
End. pool- 1 -thread- 4
End. pool- 1 -thread- 3
End. pool- 1 -thread- 5
End. pool- 1 -thread- 1
End. Finished all threads |
从输出结果看,线程池中有五个名为“pool-1-thread-1”…“pool-1-thread-5”的工作线程负责执行提交的任务。
Executors 类使用 ExecutorService 提供了一个 ThreadPoolExecutor 的简单实现,但 ThreadPoolExecutor 提供的功能远不止这些。我们可以指定创建 ThreadPoolExecutor 实例时活跃的线程数,并且可以限制线程池的大小,还可以创建自己的 RejectedExecutionHandler 实现来处理不适合放在工作队列里的任务。
下面是一个 RejectedExecutionHandler 接口的自定义实现:
RejectedExecutionHandlerImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
|
package
com.journaldev.threadpool; import
java.util.concurrent.RejectedExecutionHandler; import
java.util.concurrent.ThreadPoolExecutor; public
class RejectedExecutionHandlerImpl implements
RejectedExecutionHandler { @Override public
void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.println(r.toString() +
" is rejected" ); } } |
ThreadPoolExecutor 提供了一些方法,可以查看执行状态、线程池大小、活动线程数和任务数。所以,我通过一个监视线程在固定间隔输出执行信息。
MyMonitorThread.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
package
com.journaldev.threadpool; import
java.util.concurrent.ThreadPoolExecutor; public
class MyMonitorThread implements
Runnable { private
ThreadPoolExecutor executor; private
int seconds; private
boolean run= true ; public
MyMonitorThread(ThreadPoolExecutor executor, int
delay) { this .executor = executor; this .seconds=delay; } public
void shutdown(){ this .run= false ; } @Override public
void run() { while (run){ System.out.println( String.format( "[monitor] [%d/%d] Active: %d, Completed: %d, Task: %d, isShutdown: %s, isTerminated: %s" , this .executor.getPoolSize(), this .executor.getCorePoolSize(), this .executor.getActiveCount(), this .executor.getCompletedTaskCount(), this .executor.getTaskCount(), this .executor.isShutdown(), this .executor.isTerminated())); try
{ Thread.sleep(seconds* 1000 ); }
catch (InterruptedException e) { e.printStackTrace(); } } } } |
下面是使用 ThreadPoolExecutor 的线程池实现示例:
WorkerPool.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package
com.journaldev.threadpool; import
java.util.concurrent.ArrayBlockingQueue; import
java.util.concurrent.Executors; import
java.util.concurrent.ThreadFactory; import
java.util.concurrent.ThreadPoolExecutor; import
java.util.concurrent.TimeUnit; public
class WorkerPool { public
static void
main(String args[]) throws
InterruptedException{ //RejectedExecutionHandler implementation RejectedExecutionHandlerImpl rejectionHandler =
new RejectedExecutionHandlerImpl(); //Get the ThreadFactory implementation to use ThreadFactory threadFactory = Executors.defaultThreadFactory(); //creating the ThreadPoolExecutor ThreadPoolExecutor executorPool =
new ThreadPoolExecutor( 2 ,
4 , 10 , TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>( 2 ), threadFactory, rejectionHandler); //start the monitoring thread MyMonitorThread monitor =
new MyMonitorThread(executorPool,
3 ); Thread monitorThread =
new Thread(monitor); monitorThread.start(); //submit work to the thread pool for ( int
i= 0 ; i< 10 ; i++){ executorPool.execute( new
WorkerThread( "cmd" +i)); } Thread.sleep( 30000 ); //shut down the pool executorPool.shutdown(); //shut down the monitor thread Thread.sleep( 5000 ); monitor.shutdown(); } } |
请注意:在初始化 ThreadPoolExecutor 时,初始线程池大小设为2、最大值设为4、工作队列大小设为2。所以,如果当前有4个任务正在运行而此时又有新任务提交,工作队列将只存储2个任务和其他任务将交由RejectedExecutionHandlerImpl 处理。
程序执行的结果如下,确认了上面的结论:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
pool-1-thread-1 Start. Command = cmd0 pool-1-thread-4 Start. Command = cmd5 cmd6 is rejected pool-1-thread-3 Start. Command = cmd4 pool-1-thread-2 Start. Command = cmd1 cmd7 is rejected cmd8 is rejected cmd9 is rejected [monitor] [0 /2 ] Active: 4, Completed: 0, Task: 6, isShutdown:
false , isTerminated:
false [monitor] [4 /2 ] Active: 4, Completed: 0, Task: 6, isShutdown:
false , isTerminated:
false pool-1-thread-4 End. pool-1-thread-1 End. pool-1-thread-2 End. pool-1-thread-3 End. pool-1-thread-1 Start. Command = cmd3 pool-1-thread-4 Start. Command = cmd2 [monitor] [4 /2 ] Active: 2, Completed: 4, Task: 6, isShutdown:
false , isTerminated:
false [monitor] [4 /2 ] Active: 2, Completed: 4, Task: 6, isShutdown:
false , isTerminated:
false pool-1-thread-1 End. pool-1-thread-4 End. [monitor] [4 /2 ] Active: 0, Completed: 6, Task: 6, isShutdown:
false , isTerminated:
false [monitor] [2 /2 ] Active: 0, Completed: 6, Task: 6, isShutdown:
false , isTerminated:
false [monitor] [2 /2 ] Active: 0, Completed: 6, Task: 6, isShutdown:
false , isTerminated:
false [monitor] [2 /2 ] Active: 0, Completed: 6, Task: 6, isShutdown:
false , isTerminated:
false [monitor] [2 /2 ] Active: 0, Completed: 6, Task: 6, isShutdown:
false , isTerminated:
false [monitor] [2 /2 ] Active: 0, Completed: 6, Task: 6, isShutdown:
false , isTerminated:
false [monitor] [0 /2 ] Active: 0, Completed: 6, Task: 6, isShutdown:
true , isTerminated:
true [monitor] [0 /2 ] Active: 0, Completed: 6, Task: 6, isShutdown:
true , isTerminated:
true |
请注意活跃线程、已完成线程和任务完成总数的变化。我们可以调用 shutdown() 结束所有已提交任务并终止线程池。