java中的Thread,到底是个什么梗?

并发编程,Thread是个绕不开的梗,比如在开发过程中为啥有些地方用sleep,而有些地方又用wait来休眠,调用了sleep或wait后,又用什么方法来唤醒等等,有木有把我们的大脑整的晕头转向?所以笔者经过精心整理,梳理出Thread的核心内容供大家参考。

Thread的用法相信大家都知道,比如:

Thread thread = new Thread(){
    public void run(){
        System.out.println(this.getId()+",1,");
    }
};
thread.run();
thread.start();

直接调用Thread的run方法并不会启动线程,正确的方式是调用start方法来启动,为啥调用了start方法就能启动线程呢?进入start方法中,发现调用了start0的native方法,而这个native方法是JDK实现的,底层是调用操作系统的函数来创建线程,所以java中的线程实质就是操作系统级别的线程资源,所以创建和销毁是很好CPU资源的。

然后咱们再来看下线程有多少种状态,每种状态表示的啥意思,且看下图:

Thread类中的State已经给了明确说明:

public enum State {
    NEW,
    RUNNABLE,
    BLOCKED,
    WAITING,
    TIMED_WAITING, 
    TERMINATED;
}
  • NEW

线程创建但是还没有启动。

  • RUNNABLE

可运行线程的线程状态。处于可运行状态的线程状态正在Java虚拟机中执行,但它可能正在等待来自操作系统的其他资源,其实操作系统层面会有两种状态:准备和运行。

  • BLOCKED

阻塞,等待获取同步锁,也就是还没有进入到同步代码块里面去。

  • WAITING

等待状态,等待被唤醒,这种状态下当前线程已经释放了同步锁,这和调用sleep方法有所不同,sleep方法是不会释放同步锁的。waiting是调用了 Object.wait()、Thread.join()、LockSupport.park()方法进入的,需要被Object.notify()或Object.notifyAll()唤醒。

这里我们重点来讲下join方法的两个问题:

join方法的使用如下:

Thread thread = new Thread() {
    public void run() {
        System.out.println("test");
    }
};
thread.start(); 
try {
    thread.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("game over");

它表明调用join方法的线程,需要等待被调线程执行完毕后才执行后续的代码逻辑,如上只有当thread执行完毕后,才会执行后续代码输出game over。

那么问题来了,

问题一、这里很多人有疑问了为什么阻塞的是主调线程呢?

一般会误以为应该阻塞被调线程。我们一起来寻找下答案。

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源码,从源码来看,join方法调用的是Thread的wait方法(实际上是Object的)实现线程的阻塞。

我们在上面说过,waiting状态是在同步代码块中才有的状态,所以调用wait方法必须要获取锁的,而以代码中join方法是被synchronized修饰的,相当于synchronized(this),this就是被调线程的实例。

所以我们就可以看出,调用wait方法的主调线程获取到了锁,并且调用Thread实例的wait方法进行等待,那么阻塞的肯定就是主调线程。

问题二、主调线程在调用了join方法后,何时被唤醒呢?
在JDK的源码中,线程退出后,会调用notify_all(thread)方法来唤醒所有等待thread锁的线程,意味着调用了join方法被阻塞的主调线程会被唤醒。

  • TIMED_WAITING

调用Thread.sleep、Object.wait(long)、Thread.join、LockSupport.parkNanos、LockSupport.parkUntil等方法等待超时。

  • TERMINATED

线程执行完毕。

好了,以上就是今天的内容了,欢迎各位在评论区留言交流。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值