多线程之join方法

join方法的功能就是使异步执行的线程变成同步执行。也就是说,当调用线程实例的start方法后,这个方法会立即返回,如果在调用start方法后后需要使用一个由这个线程计算得到的值,就必须使用join方法。如果不使用join方法,就不能保证当执行到start方法后面的某条语句时,这个线程一定会执行完。而使用join方法后,直到这个线程退出,程序才会往下执行。下面的代码演示了join的用法。

package mythread;

public class JoinThread extends Thread
{
    public static int n = 0;

static synchronized void inc()
    {
        n++;
    }
    public void run()
    {
        for (int i = 0; i < 10; i++)
            try
            {
                inc();
                sleep(3);  // 为了使运行结果更随机,延迟3毫秒
                
            }
            catch (Exception e)
            {
            }                                      
    }
    public static void main(String[] args) throws Exception
    {
   
        Thread threads[] = new Thread[100];
        for (int i = 0; i < threads.length; i++)  // 建立100个线程
            threads[i] = new JoinThread();
        for (int i = 0; i < threads.length; i++)   // 运行刚才建立的100个线程
            threads[i].start();
        if (args.length > 0)  
            for (int i = 0; i < threads.length; i++)   // 100个线程都执行完后继续
                threads[i].join();
        System.out.println("n=" + JoinThread.n);
    }
}

在例程2-8中建立了100个线程,每个线程使静态变量n增加10。如果在这100个线程都执行完后输出n,这个n值应该是1000。

1.  测试1

使用如下的命令运行上面程序:

java mythread.JoinThread

程序的运行结果如下:

n=442

这个运行结果可能在不同的运行环境下有一些差异,但一般n不会等于1000。从上面的结果可以肯定,这100个线程并未都执行完就将n输出了。

2.  测试2

使用如下的命令运行上面的代码:

在上面的命令行中有一个参数join,其实在命令行中可以使用任何参数,只要有一个参数就可以,这里使用join,只是为了表明要使用join方法使这100个线程同步执行。

程序的运行结果如下:

n=1000

无论在什么样的运行环境下运行上面的命令,都会得到相同的结果:n=1000。这充分说明了这100个线程肯定是都执行完了,因此,n一定会等于1000。

多线程之join方法

join方法用得不多,当A线程执行到了B线程的join()方法时,A就会等待,等B线程都运行完,A线程才会执行。使用join()方法时,会产生异常。

用一个小程序说明join方法的使用技巧:

class Demo implements Runnable
{
 public void run()
 {
  for(int x=1;
x<100; x++)
  {
    
  System.out.println(Thread.currentThread().getName()
+ "..." + x);
  }
 }
}

public class JoinDemo
{
 public static void main(String[] args) throws
Exception
 {
  Demo d = new Demo();
  Thread t1 = new
Thread(d);
  Thread t2 = new
Thread(d);
  
  t1.start();
  
  t1.join();
  
  t2.start();
  
  for(int x=1;
x<100; x++)
  {
   System.out.println(Thread.currentThread().getName()
+ "..." + x);
  }
 }
}

主线程向下转时,碰到了t1.join(),t1要申请加入到运行中来,就是要CPU执行权。这时候CPU执行权在主线程手里,主线程就把CPU执行权给放开,陷入冻结状态。活着的只有t1了,只有当t1拿着执行权把这些数据都打印完了,主线程才恢复到运行中来。

join方法可以用于临时加入线程,一个线程在运算过程中,如果满足于条件,我们可以临时加入一个线程,让这个线程运算完,另外一个线程再继续运行。

如果将t1.join()和t2.start()位置互换,又会怎样呢?主线程开启了t1、t2,这时候CPU执行权还在主线程手里。当主线程碰到了t1.join(),释放执行权,处于冻结状态。活着的t1、t2都具备执行资格,这时候CPU便对t1和t2交替执行。主线程要等到t1结束才能活,至于t2结不结束,与主线程没有丝毫关系。如果t1结束了,t2还没结束,主线程就会和t2抢执行权执行。主线程碰到谁的join,它就等谁。也就是说,谁让它把执行权放出来,它就等谁死。至于谁去抢,它不管。

java基本教程之join方法详解 java多线程教程

本章涉及到的内容包括:
1. join()介绍
2. join()源码分析(基于JDK1.7.0_40)
3. join()示例

1. join()介绍
join() 定义在Thread.java中。
join() 的作用:让“主线程”等待“子线程”结束之后才能继续运行。这句话可能有点晦涩,我们还是通过例子去理解:

复制代码 代码如下:

// 主线程
public class Father extends Thread {
    public void run() {
        Son s = new Son();
        s.start();
        s.join();
        ...
    }
}
// 子线程
public class Son extends Thread {
    public void run() {
        ...
    }
}

说明:
上面的有两个类Father(主线程类)和Son(子线程类)。因为Son是在Father中创建并启动的,所以,Father是主线程类,Son是子线程类。

Father主线程中,通过new
Son()新建“子线程s”。接着通过s.start()启动“子线程s”,并且调用s.join()。在调用s.join()之后,Father主线程
会一直等待,直到“子线程s”运行完毕;在“子线程s”运行完毕之后,Father主线程才能接着运行。
这也就是我们所说的“join()的作用,是让主线程会等待子线程结束之后才能继续运行”!

2. join()源码分析(基于JDK1.7.0_40)

复制代码 代码如下:

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

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;
        }
    }
}

说明:
从代码中,我们可以发现。当millis==0时,会进入while(isAlive())循环;即只要子线程是活的,主线程就不停的等待。
我们根据上面解释join()作用时的代码来理解join()的用法!
问题:

然s.join()被调用的地方是发生在“Father主线程”中,但是s.join()是通过“子线程s”去调用的join()。那么,join()方
法中的isAlive()应该是判断“子线程s”是不是Alive状态;对应的wait(0)也应该是“让子线程s”等待才对。但如果是这样的
话,s.join()的作用怎么可能是“让主线程等待,直到子线程s完成为止”呢,应该是让"子线程等待才对(因为调用子线程对象s的wait方法
嘛)"?
答案:wait()的作用是让“当前线程”等待,而这里的“当前线程”是指当前在CPU上运行的线程。所以,虽然是调用子线程的wait()方法,但是它是通过“主线程”去调用的;所以,休眠的是主线程,而不是“子线程”!

3. join()示例
在理解join()的作用之后,接下来通过示例查看join()的用法。

复制代码 代码如下:

// JoinTest.java的源码
public class JoinTest{

public static void main(String[] args){
        try {
            ThreadA t1 = new ThreadA("t1"); // 新建“线程t1”

t1.start();                     // 启动“线程t1”
            t1.join();                        // 将“线程t1”加入到“主线程main”中,并且“主线程main()会等待它的完成”
            System.out.printf("%s finish\n", Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

static class ThreadA extends Thread{

public ThreadA(String name){
            super(name);
        }
        public void run(){
            System.out.printf("%s start\n", this.getName());

// 延时操作
            for(int i=0; i <1000000; i++)
               ;

System.out.printf("%s finish\n", this.getName());
        }
    }
}

运行结果:

复制代码 代码如下:

t1 start
t1 finish
main finish

结果说明:
运行流程如图
(01) 在“主线程main”中通过 new ThreadA("t1") 新建“线程t1”。 接着,通过 t1.start() 启动“线程t1”,并执行t1.join()。
(02) 执行t1.join()之后,“主线程main”会进入“阻塞状态”等待t1运行结束。“子线程t1”结束之后,会唤醒“主线程main”,“主线程”重新获取cpu执行权,继续运行。

时间: 2024-10-12 14:28:20

多线程之join方法的相关文章

多线程之Join

Join方法,其实简单的说就是阻止主线程运行,知道子线程完成后才继续执行主线程.注意这里只是阻止主线程. 下面是个例子: myThread.Join() Console.WriteLine("Child joined"); 运行结果是:先显示myThread线程内的内容,然后显示Child joined. 从这里可以看到,当调用Join方法后,Console语句需要等待myThread这个子线程执行完后才能输出(因为Console属于主线程). 多线程之Join

Java多线程之join

将另外一个线程join到当前线程,则需要等到join进来的线程执行完才会继续执行当前线程. package Thread.join; class Sleeper extends Thread { private int duration; public Sleeper(String name, int sleepTime) { super(name); duration = sleepTime; start(); } public void run() { try { sleep(duratio

线程之join()

join()    在A线程中加入B线程,这时当运行线程A时,先将其中B的线程运行完后再运行A中的后面的代码(不会交替运行),相当于A线程中的一个方法 实例: 功能:创建一个子线程,将子线程作为主线程的一部分 package com.exmaple.Thread; public class Test3 { /** * join() * @param args */ public static void main(String[] args) { // TODO Auto-generated me

线程之sleep(),wait(),yield(),join()等等的方法的区别

操作线程的常用方法大体上有sleep(),join(),yield()(让位),wait(),notify(),notifyAll(),关键字synchronized等等.    由于这些方法功能有些相似,所以有时候会混乱,我们就需要了解它们的具体的原理,以及通过自己写的具体的例子去巩固,加深印象      sleep(),yield()方法的区别:          sleep()和yield()都是Thread类中的静态方法,都会使得当前处于运行状态的线程放弃CPU,但是两者的区别还是有比较

C#多线程之Parallel中 类似于for的continue,break的方法

好久没写东西了,终于找到点知识记录下... 利用ParallelLoopState对象来控制Parallel.For函数的执行,ParallelLoopState对象是由运行时在后台创建的: Parallel.For(1, 100, (i, ParallelLoopState) =>{...}); 这个对象有两个函数,Stop和Break,可以分别用来控制Parallel.For的执行. 调用Stop,表示Parallel.For的执行立刻停止,无论其他执行单元是否达到停止的条件.而如果我们使用

JAVA多线程之wait/notify

本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait() 与  notify/notifyAll() 的执行过程 ③中断 调用wait()方法进入等待队列的 线程 ④notify 通知的顺序不能错 ⑤多线程中测试某个条件的变化用 if 还是用 while? ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 wait()

OC多线程之GCD ----- 2

dispatch_create生成的Queue不管是并行队列还是串行队列,其优先级都是默认优先级 但是可以用dispatch_set_target_queue来改变队列的优先级 dispatch_set_target_queue(原来的队列, 目标优先级队列) 使用这个函数需要获取两个队列,一个是需要变更优先级的队列,一个是指定优先级的队列(指定优先级的队列可以通过get_global获得) 如果多个串行队列优先级相同,那么这些队列里的任务也会串行执行 dispatch_after函数并不能非常

多线程之AutoResetEvent

我们在线程编程的时候往往会涉及到线程的通信,通过信号的接受来进行线程是否阻塞的操作. AutoResetEvent 允许线程通过发信号互相通信.通常,此通信涉及线程需要独占访问的资源. AutoResetEvent 的方法有很多,具体方法和扩展方法请详见AutoResetEvent类,最常用方法中就有Set()和WaitOne(). 线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号.如果 AutoResetEvent 处于非终止状态,则该线程阻塞,并等待当前控制资源的

Java多线程之wait(),notify(),notifyAll()

在多线程的情况下,因为同一进程的多个线程共享同一片存储空间,在带来方便的同一时候,也带来了訪问冲突这个严重的问题.Java语言提供了专门机制以解决这样的冲突,有效避免了同一个数据对象被多个线程同一时候訪问. wait与notify是java同步机制中重要的组成部分.结合与synchronizedkeyword使用,能够建立非常多优秀的同步模型. synchronized(this){ }等价于publicsynchronized void method(){.....} 同步分为类级别和对象级别