Thread

Thread的六种状态

  1. NEW
    Thread对象被创建,也即被初始化后,且未执行start()函数之前的状态
  2. RUNNABLE
    Thread对象调用了start()函数后,但是还未执行run()方法,例如它正在等待操作系统为其分配处理器时间片,此处又涉及了操作系统的调度知识
  3. BLOCKED
    等待获取锁对象的状态,在这种状态下,线程一直等待着获取锁对象然后进入同步方法或是同步代码块。进入BLOCKED状态的途径有:调用了wait()函数等待重新获取锁,或是在调用wait()函数之前就未获取到锁。一个是曾经拥有了,但是又放弃了,一个是未曾拥有过
  4. WAITING
    调用了Object.wait()函数或是Thread.join()函数或是LockSupport.park()函数后的状态,处于此种状态下的线程,在等待其他线程调用Object.notify()或是Object.notifyAll()。当调用了Thread.join()函数后,只能等待这个线程被terminate或是执行完成,这个过程即为WAITING
  5. TIMED_WAITING
    和WAITING类似,只是这个增加了一个时间参数
  6. TERMINATED
    线程终止的状态,当线程执行完以后,则进入这个状态。

BLOCKED和WAITING的区别

BLOCKED状态是线程阻塞在等待锁以及阻塞在进入或是重新进入同步代码块或是同步方法,例如调用了wait()函数;
而WAITING也同样是调用了wait()或是join()等方法,其实状态也是放弃了已经获取的CPU资源,等着其他线程调用notify()或是notifyAll()等方法;
难道它们的区别是:BLOCKED是等待进入同步代码或同步块去执行,WAITING是等待被唤醒?
网上多数的说法是:BLOCKED状态相关联的是同步队列,WAITING相关联的是等待队列,线程的状态变化是:由WAITING状态变化为BLOCKED状态,BLOCKED状态在等到锁后即进入RUNNABLE状态,也可以理解为BLOCKED和WAITING有一个时序的关系。

调用线程的join()函数,为什么会阻塞当前线程执行

线程是由Java虚拟机来创建和执行,并且Thread的run()方法实际上是由Java虚拟机来负责调度,run()方法运行于创建的线程中。
除了run()方法之外,Thread类本身和其他普通类没有多大区别,Thread类中的其他方法和普通类的方法也没有多大区别,对Thread类其他方法的调用都是运行于当前调用线程的虚拟机栈(也即当前调用线程中)

public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread("JoinThread");
        myThread.start();
        myThread.join();
        System.out.println("Hello World");
    }

在这种状态下,不会输出 Hello World,直到MyThread线程的run()函数执行完毕,线程退出,否则程序一直阻塞于myThread.join()函数
除了在线程中run()方法中的代码段,其他在当前线程中调用的代码段运行于当前线程(多进程,远程方法调用等除外,此处可以忽略)
myThread.start()函数执行后,MyThread线程执行自己的业务,执行逻辑立即回到main()函数线程
而随后myThread.join()函数是由当前线程,也即main()函数线程调用,myThread.join()函数运行于main()函数线程,而join()函数会持久阻塞,所以main()函数线程不会继续执行,会一直阻塞于join()函数的while循环中,直到myThread线程执行完毕后退出线程,此时join()的while循环也执行结束,main()函数线程从join()函数返回,继续执行下面的代码。

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) {
            // 如果join()函数的参数是0,则说明调用者想一直等待此线程执行结束
            while (isAlive()) {
                // 如果线程一直alive,则一直处于while循环中,直到此线程非alive才退出循环
                wait(0);
            }
        } else {
            while (isAlive()) {
                /**
                 * 如果传递的参数大于0,则判断当前时间减去join()函数初次调用的时间
                 * 是否大于需要等待的时间,如果大于需要等待的时间,则说明等待时间够了,执行break退出循环
                 * 如果是不大于需要等待的时间,则说明还得继续等待,while循环不能break
                 */
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        // while循环退出,则join()函数也执行完退出
        }
    }

join()函数运行于调用方的线程,join()函数一直阻塞于while循环中,所以调用方必须等待join()函数返回后才可执行接下来的代码,所以它才会阻塞调用方,让调用方等着它执行完成后才会执行自己的业务,这也就实现了线程插队的功能。
归根结底还是看代码段运行于哪个线程

interrupt()

interrupt()函数并不能真正结束一个线程,其只是为调用此函数的线程打上一个interrupt的标签,其函数内部会调用interrupt0(),此函数是一个native函数,api中有明确的说明:// Just to set the interrupt flag。它并不能中断一个线程,只是设置一个线程中断标志。
既然interrupt()只是设置中断标志,又没有实际作用,那这个函数有什么用呢?
这个函数其实是和sleep(), wait()等函数共同使用的,具体可参考<https://www.cnblogs.com/jiangzhaowei/p/7201244.html>

interrupted()

此函数为一个静态函数,它的作用是判断执行这段代码的线程是否处于中断状态,这个函数具体清除中断状态的作用,例如:

Thread.currentThread().interrupt();
System.out.println("Thread.interrupted() = " + Thread.interrupted());
System.out.println("Thread.interrupted() = " + Thread.interrupted());

输出的结果为:
Thread.interrupted() = true
Thread.interrupted() = false
当执行第一段Thread.interrupted()时,获取到interrupt状态,即为true,同时它又会把当执行此段代码的线程的interrupt状态重置为false状态,
所以在执行第二段Thread.interrupted()时,获取到interrupt状态是已经被重置之后的状态,即为false。

isInterrupted()

此函数为Thread类的一个实例函数,这个函数是被Thread类的对象调用,它的作用域为某一实例对象。
当Thread类对象执行interrupt()函数设置interrupt状态后,再调用isInterrupted()函数并不会重置interrupt状态,因此每次调用isInterrupted()都会返回线程的真实状态。

interrupted()和isInterrupted()的区别

  1. interrupted()属于静态方法,isInterrupted()属于实例方法
  2. interrupted()会清除interrupt状态,isInterrupted()不会清除interrupt状态
  3. interrupted()的返回值为调用此函数的线程的中断状态,isInterrupted()的返回值是某一线程对象的中断状态。

volatile关键字

volatile可以实现轻量级同步,例如一个线程写,多个线程读,涉及到多线程同时读写时,其不能保证同步。
volatile也能够禁止指令优化,从而避免了多线程环境下程序出现乱序执行的现象。
在访问被volatile修饰的变量时,会强制从主内存中获取最新的数据,这样并不会实现线程同步,因为其他线程使用volatile修饰的变量时会它加载到线程私有内存空间中计算,计算结束后会把数据更新到主内存。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351