首先回顾一下我们的基础知识。
sleep:
线程睡眠,不会释放锁
wait:
线程等待。释放锁。
notity:
唤醒随机一个当前对象等待的线程,并不会释放锁
notityAll:
唤醒所有当前对象等待的线程,并不会释放锁
遇到问题:
代码如下:
package com.zhen.ten_chapter.question; /** * @author zhen * @Date 2019/4/18 10:17 */ public class ThreadDemo { public static void main(String[] args) { To10X tos = new To10X(1, 10); tos.start(); int res = tos.getResult(); System.out.println(res); } static class To10X extends Thread { private int point = 1; private int end = 10; private int result = 0; public To10X(int start, int end) { this.point = start; this.end = end; } @Override public synchronized void run() { while (point != end + 1) { result += point++; System.out.println(Thread.currentThread().getName() + "::" + result); } this.notify(); } public synchronized int getResult() { while(result == 0) { try { System.out.println("锁定前"); this.wait(); System.out.println("锁定后"); } catch (Exception e) { e.printStackTrace(); } } return result; } } }
程序发现wait总是会被唤醒。
怀疑是不是总是先wait然后再被notity了,于是将notify注释掉了,但依然被唤醒。
什么原因?
我关注点放在了static关键字上了,依稀记得static 方法的锁锁对象是类,那么static类里面的成员方法的锁对象是不是也是类呢?
当然是我多想了,但是我依然将static 的内部类改为了一个普通内部类,然后用实例化对象去创建的对应对象与执行方法。可是结果依旧是一样。
我接下来怀疑是主线程的原因
因为其他线程都是从主线程上衍生出来的,线程不是很熟练,依稀记得一些所谓守护线程等概念。于是简单写了一个Demo类,主线程wait,然后开一个线程获取到锁执行到结束,然后发现能wait住主线程
那是什么原因呢?
接下来我觉得一定是有什么东西唤醒了主线程,notity被我注释掉了,notityAll没有编写。查看wait的api,发现它没有自动唤醒的说法,有一个参数是延迟等待的意思。它明确声明了只能靠notity和notifyAll唤醒。百度大法好。我狂百度,发现有人遇到和我一样的问题,参考链接: https://blog.csdn.net/nmyangym/article/details/7850882#commentBox 。为此,我也去看了一下所谓的jdk的Thread类的join源码,如下:
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; } } }
它确实调用了wait,但是调用了wait能保证唤醒吗?我感觉方向没问题了,继续针对点去百度
参考链接:
https://segmentfault.com/q/1010000016744022?utm_source=tag-newest
原来,Thread对象在线程结束的时候,会自动调用一次notifyAll语法,线程结束会执行join方法,join的jdk写法我们看到过,大牛给出的依据是openJDK中的源码:
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread); static void *java_start(Thread *thread) { ... thread->run(); return 0; } void JavaThread::run() { ... thread_main_inner(); } void JavaThread::thread_main_inner() { ... this->exit(false); delete this; } 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(); }
结论:
尽量不要用线程对象做同步锁的钥匙,线程结束的时候它会自动调用this.notifyAll()