当前位置: 首页 > 编程笔记 >

Thread线程的基础知识及常见疑惑点总结

昌山
2023-03-14
本文向大家介绍Thread线程的基础知识及常见疑惑点总结,包括了Thread线程的基础知识及常见疑惑点总结的使用技巧和注意事项,需要的朋友参考一下

引言

相信各位道友在平时工作中已经很少直接用到Thread线程类了,现在大多是通过线程池或者一些多线程框架来操作线程任务,但我觉得还是有必要了解清楚Thread线程类中各种方法的含义,了解了底层才能更好的理解框架、应用框架。下面我就将Thread线程的相关基础点总结一二,以供观瞻。

正文

1、Thread线程的状态

根据《深入理解Java虚拟机》一书的讲述,Java语言定义了五种线程状态,分别为:创建(new)、运行(Runnable)、等待(waiting)、阻塞(blocked)、结束(terminated)。而且规定,在某一个时间点,每个线程能且只能处于其中的一种状态。

其中,运行状态又包括就绪(Ready)跟正在运行(Running),区别就是是否获得了CPU的执行时间。

对于等待跟阻塞状态,需要着重说明一下,因为此处极易搞错,而且也是面试常被问到的点。等待状态,一般由Object.wait()、Thread.sleep()、Thread.join()、LockSupport.park()等方法以及这些方法带时间控制的同类方法实现线程的等待。而阻塞状态,一般是由于当前线程还未获取到独占锁且正在等待获取,此时称为阻塞。可以将等待看做主动的线程暂停执行,以为需要调用特定的方法线程才会等待;而阻塞可以看做是被动的线程暂定执行,因为线程在等着获取独占锁。

2、Thread线程的相关方法

start()方法/run()方法:有时在面试的时候,面试官会问到调用线程的start方法跟直接调用run方法有什么区别?虽然有的道友看到这里会觉得问这种问题的面试官有点很没必要,但我还是说一下。调用start方法后,最终会调用Thread类中的一个本地方法start0,这个方法可以新建一个线程来运行你的run方法,而调用run方法后只是在当前线程上运行你的run方法,并没有新线程参与。

wait()方法/sleep()方法:请注意,这里很多人都会记错,wait方法以及跟它配套的notify/notifyAll方法,是位于顶级父类Object下的,而其他操作线程的方法都在Thread线程类下。为什么要将wait方法放在Object下呢?其实这是由wait/notify方法的实现原理决定的。wait方法调用了之后,会释放锁,并让当前线程等待,而对于java的原生锁synchronized,是隶属于一个特定对象的监视器monitor的,那这个释放的是锁谁的锁?不能是别人的,只能是调用wait方法的那个对象的。而这个锁是哪里来的?要释放锁,肯定之前加过锁,在哪里加的呢?只能是在synchronized块中给这个对象加的,所以这也解释了为什么wait/notify方法一直要跟synchronized一起用,因为它俩就是通过操作对象的锁实现的等待和唤醒。相比而言sleep方法单纯很多,它只是让当前线程睡眠一段时间,并不会涉及到对锁的操作,所以直接放在Thread类中就行。对于wait跟notify的演示如下:

public static void main(String[] args) throws InterruptedException {
    Object obj = new Object();
    Thread thread = new Thread(new Runnable() {
      @Override
      public void run() {
        synchronized (obj) {
          try {
            System.out.println("thread获取到锁,触发wait");
            obj.wait();
            System.out.println("wait over");
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    });
    Thread thread1 = new Thread(new Runnable() {
      @Override
      public void run() {
        synchronized (obj) {
          try {
            System.out.println("thread1获取到锁");
            Thread.sleep(1000);
            System.out.println("1秒后唤醒");
            obj.notify();
          } catch (Exception e) {
            e.printStackTrace();
          }
          System.out.println("notify over");
        }

      }
    });
    thread.start();
    thread1.start();
  }

执行结果为:

thread获取到锁,触发wait
thread1获取到锁
1秒后唤醒
notify over
wait over

LockSupport.park():另外还有JUC包中的park方法让当前线程等待。此方法是使用CAS实现的线程等待,不会释放锁。而park/unpark方法比wait/notify这一对好的地方在于,前者可以先unpark在park,这是线程仍然会继续执行;而对于wait/notify,则需要通过程序控制执行顺序,一定要先wait在notify/notifyAll,否则顺序反了线程就会一直等待下去,由此悲剧诞生... 比如讲上述wait/notify的代码34行35行调换一下顺序,执行结果如下所示:

thread1获取到锁
1秒后唤醒
notify over
thread获取到锁,触发wait

仿佛云天明对程心那一千八百万年的等待

join()/yield():对于Thread下的这两个方法,之所以放在一起讲解,就是因为这两个方法平时比较少用到,属于闲云野鹤的存在。

yield()方法是让当前线程让步,让步的意思就是放弃执行权,即当前线程会从上述说的运行状态runnable中的running状态进入ready就绪状态,但是虚拟机不保证当前线程执行了yield方法后不会紧接着再次进去running状态,因为可能CPU分配执行时间时又分给了当前线程。所以这个方法其实一般也没啥用,因为效果不稳定。

join()方法是将调用join的线程插入当前线程的执行过程中,即让当前线程等待,先执行完调用join的线程,再继续执行当前线程。注意join方法不会释放锁。join的演示代码如下:

public class RunnableThread implements Runnable{
  @Override
  public void run() {
    System.out.println("runnable run");
    try {
      System.out.println("开始睡眠");
      Thread.sleep(5000);
      System.out.println("睡了5秒");
    } catch (Exception e) {
      System.out.println("runnable exception:" + e);
    }
  }

  public static void main(String[] args) throws InterruptedException {
    Object obj = new Object();
    Thread thread = new Thread(new RunnableThread());
    thread.start();
    thread.join();
    System.out.println("end");
  }
}

执行结果为:

runnable run
开始睡眠
睡了5秒
end

结束语

这次先到这里,上述说的东西,虽然很小,而且实际中不会直接用到,但是对于我们理解线程的运行机制、理解多线程框架都有好处,所以还是有必要在自己的学习地图上理解清楚。其实线程还有一个很重要的点就是线程的中断,多线程框架或者JUC包的源码中都会涉及到对线程中断的处理以及响应,这一块我会在后面梳理清楚了之后专门整理出来。最近觉得学习进入了停滞期,有点不知道从何下手,觉得需要学的东西太多。在这里,想跟各位道友讨教一下,一个资质普通的开发者,如何才能将自己的实力提升到一个比较高的层次(比如阿里的P6P7及以上?)欢迎留言赐教,在此不胜感激!

 类似资料:
  • 本文向大家介绍JavaScript基础知识及常用方法总结,包括了JavaScript基础知识及常用方法总结的使用技巧和注意事项,需要的朋友参考一下 JAVASCRIPT是AJAX技术中不可或缺的一部分,所以想学好AJAX以及现在流行的AJAX框架,学好JAVASCRIPT是最重要的。 一、基础知识: 1.document.write(""); 输出语句 2.JS中的注释为// 3.传统的HTML文

  • 本文向大家介绍Mysql基础知识点汇总,包括了Mysql基础知识点汇总的使用技巧和注意事项,需要的朋友参考一下 1.什么是SQL语句 sql语言:结构化的查询语言。(Structured Query Language),是关系数据库管理系统的标准语言。 它是一种解释语言:写一句执行一句,不需要整体编译执行。 语法特点: 1.没有“ ”,字符串使用‘ '包含 2.没有逻辑相等,赋值和逻辑相等都是=

  • 1.1 实现方法 实现方法 使用方法 示例 继承 Thread 的方法,重写 run 方法。 调用 start 方法。 调用线程池 execute 方法。 实现 Runnable 接口,实现 run 方法。 构造 Thread 类并新建 Thread 对象,调用 Thread 对象的 start 方法。 调用线程池 execute 方法。 调用 submit 方法提交。 构造 FutureTask

  • 本文向大家介绍jQuery基础知识点总结(必看),包括了jQuery基础知识点总结(必看)的使用技巧和注意事项,需要的朋友参考一下 jQuery是一个优秀的、轻量级的js库 ,它兼容CSS3,还兼容各种浏览器(IE 6.0+, FF1.5+, Safari 2.0+, Opera 9.0+),而jQuery2.0及后续版本将不再支持IE6/7/8浏览器。jQuery使用户能更方便地处理HTML(标

  • 本文向大家介绍JavaScript ES6常用基础知识总结,包括了JavaScript ES6常用基础知识总结的使用技巧和注意事项,需要的朋友参考一下 ES6 let与const及其相关 块级作用域 ES6新增了块级作用域,总结一句话大致就是:大括号{}包起来的代码块基本山都可以当做块级作用域。 常见的有 直接使用{}包起来: 函数体大括号,if-else大括号,for循环大括号,switch大括

  • 本文向大家介绍Angularjs基础知识及示例汇总,包括了Angularjs基础知识及示例汇总的使用技巧和注意事项,需要的朋友参考一下 angularjs是google开发的一款高大上的前端mvc开发框架。 Angularjs官网:https://angularjs.org/ 官网有demo,访问可能需要FQ Angularjs中国社区:http://www.angularjs.cn/ 适合初学者