Thread源码分析之join方法

join方法示例1

源码

import java.util.concurrent.TimeUnit;


public class JoinWaitTest {

    private static int a = 0;
    private static int b = 100;

    public static void main(String... args) throws InterruptedException{
        Thread t = new Thread(new WaitThread());

        t.start();
        t.join();

        System.out.println("I'm waiting for WaitThread end.");
        System.out.println("The result is " + (a + b));
    }

    static class WaitThread implements Runnable {
        @Override
        public void run() {
            try {
                for (int i = 1; i < 6; i++) {
                    TimeUnit.SECONDS.sleep(1);
                    a++;
                    System.out.println(i);
                }
            } catch (InterruptedException e) {

            }
        }
    }
}

执行结果

1

2

3

4

5

I'm waiting for WaitThread end.

The result is 105

场景描述

在很多情况下,主线程生成并启动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。

本例只是个示例,演示的就是上述的过程。

join方法示例2

源码

import java.util.concurrent.TimeUnit;


public class JoinTest {

    public static void main(String... args) throws InterruptedException {
        Thread jt = new Thread(new JoinThread());
        Thread tt = new Thread(new TimingThread());

        tt.start();
        tt.join();
        jt.start();

    }

    static class JoinThread implements Runnable {
        @Override
        public void run() {
            System.out.println("I have waited for too long.");
        }
    }

    static class TimingThread implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 6; i++) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println("Sleep end!");
                } catch (InterruptedException e) {

                }
            }
        }
    }
}

执行结果

Sleep end!

Sleep end!

Sleep end!

Sleep end!

Sleep end!

Sleep end!

I have waited for too long.

场景描述

跟源码示例1大同小异,只不过这次是jt线程要等待tt线程结束。

join不带参数源码分析2--必须在同步方法/同步代码块中调用wait方法------为什么wait()【还有notify(),notifyAll()】必须在同步方法/代码块中调用

    在 Java中,所有对象都能够被作为"监视器monitor"——指一个拥有一个独占锁,一个入口队列和一个等待队列的实体entity。

  所有对象的非同步方法都能够在任意时刻被任意线程调用,此时不需要考虑加锁的问题。

  而对于对象的同步方法来说,在任意时刻有且仅有一个拥有该对象独占锁的线程能够调用它们。例如,一个同步方法是独占的。如果在线程调用某一对象的同步方法时,对象的独占锁被其他线程拥有,那么当前线程将处于阻塞状态,并添加到对象的入口队列中。

    只有在调用线程拥有某个对象的独占锁时,才能够调用该对象的wait(),notify()和notifyAll()方法。如果尝试在未获取对象锁时调用这三个方法,那么你将得到一个"java.lang.IllegalMonitorStateException:current thread not owner"。

    当一个线程正在某一个对象的同步方法中运行时调用了这个对象的wait()方法,那么这个线程将释放该对象的独占锁并被放入这个对象的等待队列,(JZ:意味着其他线程也可以再次调用同一个对象的wait方法)

  注意,wait()方法强制当前线程释放对象锁。这意味着线程在调用某对象的wait()方法之前,当前线程必须已经获得该对象的锁。因此,线程必须在某个对象的同步方法或同步代码块中才能调用该对象的wait()方法。wait方法是native方法,里面的实现细节不清楚,但是线程A调用某对象B的wait方法肯定默认线程A已经拥有了某对象B的锁(能够进入同步方法或同步代码块就说明线程A竞争到了对象锁)。如果跟这种默认(竞争得到锁)冲突,那么JVM就会报出运行时错误IllegalMonitorStateException(RuntimeException)

  当某线程调用某对象的 notify()或notifyAll()方法时,任意一个(对于notify())或者所有(对于notifyAll())在该对象的等待队列中的线程,将被转移到该对象的入口队列。接着这些队列(译者注:可能只有一个)将竞争该对象的锁,最终获得锁的线程继续执行。

如果没有线程在该对象的等待队列中等待获得锁,那么notify()和notifyAll()将不起任何作用。在调用对象的notify()和notifyAll()方法之前,调用线程必须已经得到该对象的锁。因此,必须在某个对象的同步方法或同步代码块中才能调用该对象的notify()或notifyAll()方法。

  对于处于某对象的等待队列中的线程,只有当其他线程调用此对象的notify()或notifyAll()方法时才有机会继续执行。

  调用wait()方法的原因通常是,调用线程希望某个特殊的状态(或变量)被设置之后再继续执行。调用notify()或notifyAll()方法的原因通常是,调用线程希望告诉其他等待中的线程:"特殊状态已经被设置"。这个状态作为线程间通信的通道,它必须是一个可变的共享状态(或变量)。

例如,生产者线程向缓冲区中写入数据,消费者线程从缓冲区中读取数据。消费者线程需要等待直到生产者线程完成一次写入操作。生产者线程需要等待消费者线程完成一次读取操作。假设wait(),notify(),notifyAll()方法不需要加锁就能够被调用。

此时消费者线程调用wait()正在进入状态变量的等待队列(译者注:可能还未进入)。在同一时刻,生产者线程调用notify()方法打算向消费者线程通知状态改变。那么此时消费者线程将错过这个通知并一直阻塞。因此,对象的wait(),notify(),notifyAll()方法必须在该对象的同步方法或同步代码块中被互斥地调用。

[JZ]:这也是多线程编程里的一个经典问题,线程A进行锁操作的过程是非原子的,线程B就进行了释放的请求,导致线程A申请锁后无法释放!

参考网页

http://www.cnblogs.com/jiangz222/p/4719671.html

http://www.blogjava.net/freeman1984/archive/2011/10/14/361306.html

join方法源码分析

jdk源码

/**

     * 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不带参数源码分析1--实质上是调用了wait方法

join()方法实质上就是join(0),最终执行代码如下所示

实质上调用代码1就相当于调用代码2

join带参数源码分析

核心代码

变一下:

while (isAlive()) {

            if (millis <= now) {

                break;

            }

            wait(millis - now);

            now = System.currentTimeMillis() - base;

        }

分析

base和now都是时间。base是刚执行代码时的时间,now是执行代码流逝的时间。判断mills时间是否流逝完毕,流逝完毕则break跳出代码。

 

 

 

转载于:https://my.oschina.net/u/3866531/blog/2242220

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值