文章目录
在使用 join 方法, 它到底是如何去唤醒线程的呢, 希望这篇文章可以帮助到你解决问题
析构函数简介
析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。
wait/notify 简单介绍
- wait():作用是使当前线程从调用处中断并且释放锁转入等待队列,直到收到notify或者notifyAll的通知才能从等待队列转入锁池队列,没有收到停止会一直死等。
- wait(long time):相比wait多了一个等待的时间time,如果经过time(毫秒)时间后没有收到notify或者notifyAll的通知,自动从等待队列转入锁池队列。
- notify():随机从等待队列中通知一个持有相同锁的一个线程,如果没有持有相同锁的wait线程那么指令忽略无效。注意是持有相同锁,并且是随机没有固定的,顺序这一点在生产者消费者模型中很重要,会造成假死的状态。
- 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: https://blog.csdn.net/weixin_43808717/article/details/115351888
- 参考2: https://blog.csdn.net/succing/article/details/123023851
文末总结
以前还不懂为啥用个 join 就能阻塞线程, 然后其他线程跑完 main
线程才会继续走后面的流程, 原来是因为在其他线程退出的时候去调用了析构函数啊。