浅析Java的Thread.join()方法

现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?

用Join可以实现。用join实现线程的执行顺序。

主线程生成并起动了子线程,而子线程里要进行大量的耗时的运算(这里可以借鉴下线程的作用),当主线程处理完其他的事务后,需要用到子线程的处理结果,这个时候就要用到 "子线程对象.join();"方法了,主线程会等子线程执行完后再执行。

JDK是这样说的:
   

Joins

The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,

t.join();

causes the current thread to pause execution until t's thread terminates. Overloads of join allow the programmer to specify a waiting period. However, as with sleepjoin is dependent on the OS for timing, so you should not assume that join will wait exactly as long as you specify.

Like sleepjoin responds to an interrupt by exiting with an InterruptedException.


    如果在线程a中执行t.join()方法,则a会等t线程执行完之后再执行t.join后的代码。换句话说,谁用obj.join()方法,谁就有执行权限,会一直执行完任务。但前提是必须能够拿到线程obj对象的锁。

join的JDK代码:

public final void join() throws InterruptedException {
    join(0);
}


就是说如果是t.join() = t.join(0) 0 JDK这样说的 A timeout of 0 means to wait forever 字面意思是永远等待,其实是等到t结束后。

    /**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }



Join方法实现是通过wait(小提示:Object 提供的方法)。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程,比如退出后。


例子1:

package com.lee.thread;

class CustomThread1 extends Thread {
	public CustomThread1() {
		super("[CustomThread1] Thread");
	};

	public void run() {
		String threadName = Thread.currentThread().getName();
		System.out.println("CustomThread1: "+threadName + " start.");
		try {
			for (int i = 0; i < 5; i++) {
				System.out.println(threadName + " loop at " + i);
				Thread.sleep(1000);
				//this.join();
			}
			System.out.println("CustomThread1: "+threadName + " end.");
		} catch (Exception e) {
			System.out.println("Exception from " + threadName + ".run");
		}
	}
}

class CustomThread extends Thread {
	CustomThread1 t1;

	public CustomThread(CustomThread1 t1) {
		super("[CustomThread] Thread");
		this.t1 = t1;
	}

	public void run() {
		String threadName = Thread.currentThread().getName();
		System.out.println("CustomThread: "+threadName + " start.");
		try {
			//System.out.println("start CustomThread join");
			for (int i = 0; i < 5; i++) {
				System.out.println(threadName + " Custom at " + i);
				
			}
			t1.join();
			for (int i = 6; i < 10; i++) {
				System.out.println(threadName + " Custom at " + i);
				
			}
			System.out.println("CustomThread: "+threadName + " end.");
		} catch (Exception e) {
			System.out.println("Exception from " + threadName + ".run");
		}
	}
}

public class JoinTestDemo {
	public static void main(String[] args) throws InterruptedException {
		String threadName = Thread.currentThread().getName();
		System.out.println("JoinTestDemo: "+threadName + " start.");
		CustomThread1 t1 = new CustomThread1();
		CustomThread ct = new CustomThread(t1);
		try {
			t1.start();
			Thread.sleep(2000);
			
			ct.start();
			//ct.join();
		} catch (Exception e) {
			System.out.println("Exception from main");
		}
		System.out.println("JoinTestDemo: "+threadName + " end!");
	}
}


结果:
JoinTestDemo: main start.
CustomThread1: [CustomThread1] Thread start.
[CustomThread1] Thread loop at 0
[CustomThread1] Thread loop at 1
JoinTestDemo: main end!	// Thread.sleep(2000);结束,虽然在线程CustomThread执行了t1.join();,但这并不会影响到其他线程(这里main方法所在的线程)。
CustomThread: [CustomThread] Thread start.	//线程CustomThread起动,但没有马上结束,因为调用t1.join();,所以要等到t1结束了,此线程才能向下执行。
[CustomThread] Thread Custom at 0
[CustomThread] Thread Custom at 1
[CustomThread] Thread Custom at 2
[CustomThread] Thread Custom at 3
[CustomThread] Thread Custom at 4
[CustomThread1] Thread loop at 2	
[CustomThread1] Thread loop at 3
[CustomThread1] Thread loop at 4
CustomThread1: [CustomThread1] Thread end.
[CustomThread] Thread Custom at 6
[CustomThread] Thread Custom at 7
[CustomThread] Thread Custom at 8
[CustomThread] Thread Custom at 9
CustomThread: [CustomThread] Thread end.


例子2:
package com.lee.thread;

class CustomThread3 extends Thread {
	public CustomThread3() {
		super("[CustomThread3] Thread");
	};

	public void run() {
		String threadName = Thread.currentThread().getName();
		System.out.println("CustomThread3: "+threadName + " start.");
		try {
			for (int i = 0; i < 5; i++) {
				System.out.println(threadName + " loop at " + i);
				Thread.sleep(1000);
				//this.join();
			}
			System.out.println("CustomThread3: "+threadName + " end.");
		} catch (Exception e) {
			System.out.println("Exception from " + threadName + ".run");
		}
	}
}

class CustomThread4 extends Thread {
	CustomThread3 t1;

	public CustomThread4(CustomThread3 t1) {
		super("[CustomThread4] Thread");
		this.t1 = t1;
	}

	public void run() {
		String threadName = Thread.currentThread().getName();
		System.out.println("CustomThread4: "+threadName + " start.");
		try {
			//System.out.println("start CustomThread4 join");
			for (int i = 0; i < 5; i++) {
				System.out.println(threadName + " Custom at " + i);
				
			}
			t1.join();
			for (int i = 6; i < 10; i++) {
				System.out.println(threadName + " Custom at " + i);
				
			}
			System.out.println("CustomThread4: "+threadName + " end.");
		} catch (Exception e) {
			System.out.println("Exception from " + threadName + ".run");
		}
	}
}

public class JoinTestDemo2 {
	public static void main(String[] args) throws InterruptedException {
		String threadName = Thread.currentThread().getName();
		System.out.println("JoinTestDemo2: "+threadName + " start.");
		CustomThread3 t1 = new CustomThread3();
		CustomThread4 ct = new CustomThread4(t1);
		try {
			t1.start();
			Thread.sleep(2000);
			ct.start();
			ct.join();
		} catch (Exception e) {
			System.out.println("Exception from main");
		}
		System.out.println("JoinTestDemo2: "+threadName + " end!");
	}
}


结果:
JoinTestDemo2: main start.
CustomThread3: [CustomThread3] Thread start.
[CustomThread3] Thread loop at 0
[CustomThread3] Thread loop at 1
[CustomThread3] Thread loop at 2
CustomThread4: [CustomThread4] Thread start.	//线程CustomThread起动,但没有马上结束,因为调用t1.join();,所以要等到t1结束了,此线程才能向下执行。
[CustomThread4] Thread Custom at 0
[CustomThread4] Thread Custom at 1
[CustomThread4] Thread Custom at 2
[CustomThread4] Thread Custom at 3
[CustomThread4] Thread Custom at 4
[CustomThread3] Thread loop at 3
[CustomThread3] Thread loop at 4
CustomThread3: [CustomThread3] Thread end.
[CustomThread4] Thread Custom at 6
[CustomThread4] Thread Custom at 7
[CustomThread4] Thread Custom at 8
[CustomThread4] Thread Custom at 9
CustomThread4: [CustomThread4] Thread end.	// 线程CustomThread在t1.join();阻塞处起动,向下继续执行的结果
JoinTestDemo2: main end!	//线程CustomThread4结束,此线程在t.join();阻塞处起动,向下继续执行的结果。


join(long)可以加join的时间
例子3:

package concurrentstudy;

public class JoinTest {
    public static void main(String[] args) {
        Thread t = new Thread(new RunnableImpl());
        t.start();
        try {
            t.join(1000);
            System.out.println("joinFinish");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
     
        }
    }
}
class RunnableImpl implements Runnable {

    @Override
    public void run() {
        try {
            System.out.println("Begin sleep");
            Thread.sleep(1000);
           System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}


结果:

Begin sleep
End sleep
joinFinish

当main线程调用t.join时,main线程等待t线程,等待时间是1000,如果t线程Sleep 2000呢


例子4:
public void run() {
        try {
            System.out.println("Begin sleep");
            // Thread.sleep(1000);
            Thread.sleep(2000);
           System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


结果:
Begin sleep
joinFinish
End sleep


也就是说main线程只等1000毫秒,不管T什么时候结束

注意:main 线程调用t.join时,必须能够拿到线程t对象的锁,如果拿不到它是无法wait的,刚开的例子t.join(1000)不是说明了main线程等待1秒,如果在它等待之前,其他线程获取了t对象的锁,它等待时间可不就是1毫秒了。

package concurrentstudy;

public class JoinTest {
    public static void main(String[] args) {
        Thread t = new Thread(new RunnableImpl());
       new ThreadTest(t).start();
        t.start();
        try {
            t.join();
            System.out.println("joinFinish");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();     
        }
    }
}
class ThreadTest extends Thread {
    Thread thread;

    public ThreadTest(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
        holdThreadLock();
    }

    public void holdThreadLock() {
        synchronized (thread) {
            System.out.println("getObjectLock");
            try {
                Thread.sleep(9000);

            } catch (InterruptedException ex) {
             ex.printStackTrace();
            }
            System.out.println("ReleaseObjectLock");
        }
    }
}

class RunnableImpl implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("Begin sleep");
            Thread.sleep(2000);
           System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


在main方法中 通过new ThreadTest(t).start();实例化ThreadTest 线程对象, 它在holdThreadLock()方法中,通过 synchronized (thread),获取线程对象t的锁,并Sleep(9000)后释放,这就意味着,即使
main方法t.join(1000),等待一秒钟,它必须等待ThreadTest 线程释放t锁后才能进入wait方法中,它实际等待时间是9000+1000 MS


结果:
getObjectLock
Begin sleep
End sleep
ReleaseObjectLock
joinFinish


参考:
http://www.blogjava.net/jnbzwm/articles/330549.html

http://www.blogjava.net/vincent/archive/2008/08/23/223912.html


转载请注明:http://blog.csdn.net/paincupid/article/details/47393563

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值