关于join唤醒线程与c++析构函数有关这件事我想了许久

在使用 join 方法, 它到底是如何去唤醒线程的呢, 希望这篇文章可以帮助到你解决问题

析构函数简介

析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。

wait/notify 简单介绍

  1. wait():作用是使当前线程从调用处中断并且释放锁转入等待队列,直到收到notify或者notifyAll的通知才能从等待队列转入锁池队列,没有收到停止会一直死等。
  2. wait(long time):相比wait多了一个等待的时间time,如果经过time(毫秒)时间后没有收到notify或者notifyAll的通知,自动从等待队列转入锁池队列。
  3. notify():随机从等待队列中通知一个持有相同锁的一个线程,如果没有持有相同锁的wait线程那么指令忽略无效。注意是持有相同锁,并且是随机没有固定的,顺序这一点在生产者消费者模型中很重要,会造成假死的状态。
  4. notifyAll():通知等待队列中的持有相同锁的所有线程,让这些线程转入锁池队列。如果没有持有相同锁的wait线程那么指令忽略无效。

    PS: 上述介绍只是对这几个方法的一些简单的介绍, 博主实力有限,这篇文章主要是想介绍一下 c++ 的析构函数, 关于 wait/notify具体的实现原理 在后续的文章中我再详细介绍, 敬请期待。

验证join操作锁的是Thread 还是 Object对象

public class JoinTest3 {

    public static void main(String[] args) {
        Object object = new Object();
        MyThread mythread = new MyThread("mythread ", object);
        mythread.start();
//        synchronized (mythread)
        synchronized (object)
        {
            for (int i = 0; i < 20; i++) {
                if (i == 10) {
                    try {
                        System.out.println("开始join");
                        mythread.join();//main主线程让出CPU执行权,让mythread子线程优先执行
                        System.out.println("结束join");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() +"==" + i);
            }
        }
        System.out.println("main方法执行完毕");
    }
}


class MyThread extends Thread {
    private String name;
    private Object obj;

    public MyThread(String name, Object obj) {
        this.name = name;
        this.obj = obj;
    }

    @Override
    public void run() {
        synchronized (obj) {
            for (int i = 0; i < 20; i++) {
                System.out.println(name + i);
            }
        }
    }
}

注释掉 synchronized (object)时锁thread时的表现:

main==0
main==1
main==2
main==3
main==4
main==5
main==6
main==7
main==8
main==9
开始join
mythread 0
mythread 1
mythread 2
mythread 3
mythread 4
mythread 5
mythread 6
mythread 7
mythread 8
mythread 9
mythread 10
mythread 11
mythread 12
mythread 13
mythread 14
mythread 15
mythread 16
mythread 17
mythread 18
mythread 19
结束join
main==10
main==11
main==12
main==13
main==14
main==15
main==16
main==17
main==18
main==19
main方法执行完毕

注释掉 synchronized (mythread)时锁object时的表现:

main==0
main==1
main==2
main==3
main==4
main==5
main==6
main==7
main==8
main==9
开始join

join()会释放thread的锁,不会释放线程对象的锁(可能会造成死锁)。 join操作锁的是 thread。

c++的退出线程方法

void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
	// 省略代码
	  ensure_join(this); // ensure_join是线程退出时调用(内部会调用notifyAll)
   // 省略代码 	  
}

thread.cpp#ensure_join

static void ensure_join(JavaThread* thread) {
  Handle threadObj(thread, thread->threadObj());
  assert(threadObj.not_null(), "java thread object must exist");
  ObjectLocker lock(threadObj, thread); // 创建ObjectLocker(构造函数加锁,析构函数解锁)
  thread->clear_pending_exception();  // 清除待处理异常,因为线程准备退出了
  java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED); // 设置线程状态为 TERMINATED
  java_lang_Thread::set_thread(threadObj(), NULL);// 将属性设置为null, 这样 is_alive方法就会返回 false
  lock.notify_all(thread); // 唤醒所有关联的重量级锁上等待的线程
  thread->clear_pending_exception(); // 清除待处理异常(走到这个地方 ObjectLocker生命周期结束,自动调用析构函数)
}

synchronizer.cpp

在学习 线程通信的时候发现 synchronizer.cpp 这段代码挺有意思的。小伙伴们在学习 wait/notify/notifyAll 肯定会遇到的,故这里演示一下。

ObjectLocker::ObjectLocker(Handle obj, Thread* thread, bool doLock) {
  _dolock = doLock;
  _thread = thread;
  debug_only(if (StrictSafepointChecks) _thread->check_for_valid_safepoint_state(false);)
  _obj = obj;

  if (_dolock) {
    TEVENT (ObjectLocker) ;

    ObjectSynchronizer::fast_enter(_obj, &_lock, false, _thread);// 构造函数加锁
  }
}

ObjectLocker::~ObjectLocker() { // 析构函数(一般用来和构造函数执行相反的动作)
  if (_dolock) {
    ObjectSynchronizer::fast_exit(_obj(), &_lock, _thread);
  }
}

测试析构函数代码

#include <iostream>

using namespace std;

class Test{
public:
	void setLength(double length);
	double getLength(void);
	Test();
	~Test();

private:
	double length;
};
Test::Test(void) {
	cout << "=======begin=======";
}
Test::~Test(void) {
	cout << "=======end=========";
}
void Test::setLength(double length) {
	length = length;
}
double Test::getLength(void) {
	return length;
}

int main() {
	Test test;

	test.setLength(6.0);
	cout << "length: " << test.getLength() << endl;
	return 0;
}

`执行如下指令

g++ -o Test Test.cpp
./Test

输出结果

=======begin=======
length: 2.0736e-317
=======end=========

参考资料

  1. 参考1: https://blog.csdn.net/weixin_43808717/article/details/115351888
  2. 参考2: https://blog.csdn.net/succing/article/details/123023851

文末总结

以前还不懂为啥用个 join 就能阻塞线程, 然后其他线程跑完 main 线程才会继续走后面的流程, 原来是因为在其他线程退出的时候去调用了析构函数啊。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值