有时候我们会遇到这样的问题:做一个大的事情可以被分解为做一系列相似的小的事情,而小的事情无非就是参数上有可能不相同而已!
此时,如果不使用线程,我们势必会浪费非常多的时间来完成整个大的事情,而使用线程的话将会存在这样的问题:
主线程启动所有子线程并发执行后主线程就直接返回了,导致外部函数判读整个大的事情完成了,但是实际上并没有完成!
针对以上情况我想我会采用多线程方式执行同时解决主线程等待子线程的问题。如图:
在这里我使用Java进行案例分析。
首先建立一个线程管理类,用于启动所有子线程和等待所有子线程完成,在这里不使用休眠一段时间后循环检测的方式(消耗CUP同时消耗时间,全部完成时间不够及时等缺点);而是使用等待临界值的方式。ThreadManager.java如下:
public class ThreadManager implements NotifyInterface { private final Object mLock = new Object(); private int mCount = 0; private int endCount = 0; public ThreadManager(int count) { System.out.println("Manager In."); this.mCount = count; this.addThread(); synchronized (mLock) { while (true) { if (checkEnd()) break; try { mLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } System.out.println("Manager Out."); } private void addThread() { System.out.println("Manager addThread()."); for (int i = 1; i <= mCount; i++) { ThreadDoThing dThread = new ThreadDoThing(i, "T" + i, this); // Start dThread.start(); } } private boolean checkEnd() { boolean bFlag = false; bFlag = endCount >= mCount; System.out.println("Manager checkEnd().Return is:" + bFlag); return bFlag; } @Override public void runEnd() { synchronized (mLock) { ++endCount; mLock.notifyAll(); } } }
此类集成自:NotifyInterface接口,NotifyInterface是用于子线程通知主线程自己已经完成工作所用类,ThreadManager实例化时将传入一个int值,用于设置启动的子线程数,当然这里是为了简单介绍所以采用的这样的方式,实际情况可能更加复杂。
在实例化后 进入构造方法,此时将会启动子线程,启动后进入循环等待中,当检测到所有子线程完成时就退出循环,没有就将进入临界值等待,直到通过接口通知主线程完成时将会通知临界值一次,此时循环将会执行一次,如果不满足退出条件将继续等待临界值。直到满足为止。
NotifyInterface接口如下:
public interface NotifyInterface { public abstract void runEnd(); }
测试用的子线程ThreadDoThing.java如下:
public class ThreadDoThing extends Thread { private NotifyInterface mInterface = null; private int mId = 0; private String mArgs = null; public ThreadDoThing(int id, String args, NotifyInterface iface) { this.mId = id; this.mArgs = args; this.AddInterface(iface); } public void AddInterface(NotifyInterface iface) { this.mInterface = iface; } @Override public void run() { System.out.println("ThreadDoThing Id is:" + this.mId + " Args is:" + this.mArgs); System.out.println(this.mId + ":Doing..."); int sleepTime = (int) (Math.random() * 1000); try { Thread.sleep(sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.mId + ":SleepTime is:" + sleepTime); this.notifyEnd(); System.out.println(this.mId + ":Do End."); } private void notifyEnd() { if (this.mInterface != null) this.mInterface.runEnd(); System.out.println(this.mId + ":Notify End."); } }
此类继承自Thread类,可直接重写Run()方法完成所做工作!
在工作中,我使用了随机一个1s内的休眠来代替所做工作的时间,完成后调用接口通知完成。
测试方法如下:
/** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub ThreadManager manager = new ThreadManager(10); }
测试结果:
Manager In. Manager addThread(). ThreadDoThing Id is:1 Args is:T1 ThreadDoThing Id is:2 Args is:T2 2:Doing... 1:Doing... ThreadDoThing Id is:3 Args is:T3 ThreadDoThing Id is:4 Args is:T4 3:Doing... 4:Doing... ThreadDoThing Id is:5 Args is:T5 5:Doing... ThreadDoThing Id is:6 Args is:T6 Manager checkEnd().Return is:false ThreadDoThing Id is:8 Args is:T8 ThreadDoThing Id is:7 Args is:T7 8:Doing... ThreadDoThing Id is:9 Args is:T9 9:Doing... 6:Doing... ThreadDoThing Id is:10 Args is:T10 7:Doing... 10:Doing... 3:SleepTime is:111 3:Notify End. 3:Do End. Manager checkEnd().Return is:false 5:SleepTime is:142 5:Notify End. Manager checkEnd().Return is:false 5:Do End. 4:SleepTime is:199 4:Notify End. Manager checkEnd().Return is:false 4:Do End. 7:SleepTime is:342 7:Notify End. Manager checkEnd().Return is:false 7:Do End. 10:SleepTime is:346 10:Notify End. Manager checkEnd().Return is:false 10:Do End. 6:SleepTime is:397 6:Notify End. Manager checkEnd().Return is:false 6:Do End. 9:SleepTime is:468 9:Notify End. Manager checkEnd().Return is:false 9:Do End. 1:SleepTime is:475 1:Notify End. Manager checkEnd().Return is:false 1:Do End. 2:SleepTime is:686 Manager checkEnd().Return is:false 2:Notify End. 2:Do End. 8:SleepTime is:828 8:Notify End. Manager checkEnd().Return is:true 8:Do End. Manager Out.
实际情况可能更加复杂,甚至子线程下还有更多的子线程!
具体情况大家可以衍生考虑,检测是否全部返回也可以有多种方式甚至设置添加一个定时器之类的。
以后有时间画一个详细点的图!