zoukankan      html  css  js  c++  java
  • [Java][Android] 多线程同步-主线程等待全部子线程完毕案例

    有时候我们会遇到这种问题:做一个大的事情能够被分解为做一系列相似的小的事情,而小的事情无非就是參数上有可能不同样而已!

    此时,假设不使用线程,我们势必会浪费许多的时间来完毕整个大的事情。而使用线程的话将会存在这种问题:

    主线程启动全部子线程并发运行后主线程就直接返回了,导致外部函数判读整个大的事情完毕了,可是实际上并没有完毕!


    针对以上情况我想我会採用多线程方式运行同一时候解决主线程等待子线程的问题。如图:


    在这里我使用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.

    实际情况可能更加复杂。甚至子线程下还有很多其它的子线程!

    详细情况大家能够衍生考虑,检測是否所有返回也能够有多种方式甚至设置加入一个定时器之类的。

    以后有时间画一个具体点的图!


  • 相关阅读:
    dig批量获取域名对应IP
    文件和目录
    Linux程序设计的CD唱片应用程序
    LinuxRedhat7.0虚拟机配置双网卡
    Redhat7.0计划任务服务程序(at,crontab)
    RedHat7 修改主机名称 配置网卡信息 配置Yum软件仓库
    关于RedHat5.0不能提示找不到/media/cdrom/repodate/repomd.xml
    Redhat5静态IP分配,提示Error, some other host already uses address解决办法
    三种时间戳的解释
    RHEL 7 -解决“没有启用回购”消息
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/7154749.html
Copyright © 2011-2022 走看看