线程中的join方法,与synchronized和wait()和notify()的关系

        什么时候要用join()方法?

        1,join方法是Thread类中的方法,主线程执行完start()方法,线程就进入就绪状态,虚拟机最终会执行run方法进入运行状态.此时.主线程跳出start方法往下执行

        2,两个线程以上,当一个线程需要另一个线程执行的结果时,可以在该线程之前调用另一个线程对象的join方法,如下:

public class TestThread {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new ThreadA("ThreadA");
        Thread t2 = new Thread(new ThreadB(),"ThreadB");
        t1.start();//主线程调用start方法,开启t1线程,
        t1.join();//主线程调用join方法,获取对象t1对象锁,挂起主线程.直到t1线程结束
        t2.start();//主线程调用start方法,开启t2线程
        t2.join();//主线程调用join,获取t2对象锁,挂起主线程,直到t2线程结束
        System.out.println(Thread.currentThread().getName()+"主线程结束");
    }
}
如上代码,主线程一路下来调用其他线程的对象的join方法,就会被挂起直到该线程对象所在的线程结束.从而实现线程的顺序执行.

       

        查看join的源码,该方法是同步方法,锁是t1线程所在对象的对象锁,我们可以看到,wait总是在synchronized代码块里面使用,并且在while循环中,有wait()就必然有notify()/notifyAll(),所以同样的一般情况下notify()/notifyAll()也是在synchronized代码块中的while使用的,synchronized,while,wait,notify结合可以实现经典的生产者消费者模式.

public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        ....
        //主线程调用join方法就拥有了t1对象的锁,wait(0)一直处于等待状态,
        //直到某个线程调用了该对象的notify或者notifyAll()
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
           ....
        }
    }
        
在join方法中我们看到了wait(),那么一定在某个地方有notify方法,该方法则在jvm源码中,这个段源码的作用是结束线程,在线程退出前需要做一些动作,其中就有调用notify_all的动作,看注释,还将isAlive变为false,
// 位于/hotspot/src/share/vm/runtime/thread.cpp中
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
    // ...
    // Notify waiters on thread object. This has to be done after exit() is called
    // on the thread (if the thread is the last thread in a daemon ThreadGroup the
    // group should have the destroyed bit set before waiters are notified).
    // 有一个贼不起眼的一行代码,就是这行
    ensure_join(this);
    // ...
}
static void ensure_join(JavaThread* thread) {
    // We do not need to grap the Threads_lock, since we are operating on ourself.
    Handle threadObj(thread, thread->threadObj());
    assert(threadObj.not_null(), "java thread object must exist");
    ObjectLocker lock(threadObj, thread);
    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
    // Thread is exiting. So set thread_status field in  java.lang.Thread class to TERMINATED.
    java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
    // Clear the native thread instance - this makes isAlive return false and allows the join()
    // to complete once we've done the notify_all below
    java_lang_Thread::set_thread(threadObj(), NULL);
    // 唤醒其他线程
    lock.notify_all(thread);
    // Ignore pending exception (ThreadDeath), since we are exiting anyway
    thread->clear_pending_exception();
}
 

  由此我们即看到了notify,也看到了isAlive变为false,所以主线程再挂起的时候,会等待该对象线程t1结束并调用notify,唤醒之后则跳出循环检测isAlive为false则跳

出循环结束join方法,这就实现了线程的顺序执行,

码解读完毕

        从代码中的while(isAlive){wait()}说起,

      如果主线程在等待状态,被其他线程唤醒呢,所以前文我说,notify也一般配合synchronized,while使用,这个一般情况下是消费者生产者模式,但是在jvm源码中并没

有这么用,这是要等待t1线程结束前调用,所以这个isAlive就是保证一定被t1线程结束唤醒,而其他线程唤醒while判断之后再次进入挂起状态.

几个问题的辨析, 

        1,线程之间的竞争是同一个对象锁的竞争或者同一个类锁的竞争,与其他对象的锁无关.其他线程抢别的对象的锁,执行别对象的wait/notify方法都与竞争本对象

锁的线程无关.

       2, 锁分为对象锁,类锁,锁的使用者叫线程,锁拦截的对象也是线程,不是什么方法,或者其他,.

        对象锁,表示对象里面所有被synchronized(this)修饰的代码块和synchronized 修饰的普通方法块,不能同时被两个线程执行.详情见:synchronized的使用方法和作用域:

https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554746&idx=1&sn=8e45e741ca9f058dba1f3ebbea0e9f07&chksm=f3f833ecc48fbafa295e355c1cdd52dc4259f161dafdc1703d181a5e9f4f76563c98493bd221&token=2005887224&lang=zh_CN#rd

        类锁是被synchronized(MyClass.class)和static synchronized 修饰的静态方法,不能同时被两个线程执行,详情见:synchronized的使用方法和作用域        

      https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554746&idx=1&sn=8e45e741ca9f058dba1f3ebbea0e9f07&chksm=f3f833ecc48fbafa295e355c1cdd52dc4259f161dafdc1703d181a5e9f4f76563c98493bd221&token=2005887224&lang=zh_CN#rd

        3,线程释放锁的场景,

        a,执行完同步代码块,

        b,在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放

        c在执行同步代码块的过程中,执行了锁所属对象的wait方法,这个线程会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒

notify和notifyAll的区别

        我们首先得明确,一个对象只有一个锁,参与该对象锁竞争的线程,与参与其他对象锁竞争的线程无关.线程竞争,对应一个对象的一个锁.

        有n个线程参与竞争同一对象的锁,t1率先抢到了锁,锁池中有n-1个线程在等待竞争锁,t1调用了wait()方法t1线程挂起,等待池中有一线程等待被唤醒.t2线程,获得了锁,锁池中有n-2个线程等待竞争锁,t2线程调用了wait方法挂起,等待池中有两个线程等待被唤醒,

此时t3线程获得了锁,

        如果t3线程在同步代码块中调用的是notify则只会随机唤醒等待池中的一个线程移入锁池中参与锁竞争,有可能是t1,也有可能是t2,等待池中的线程为1

        如果t3线程在同步代码块中调用的notifyAll,则会同时唤醒t1,t2移入锁池中参与竞争,等待池中的线程为零,

公众号:

猜你喜欢

转载自www.cnblogs.com/qq289736032/p/10716215.html