本博客系列是学习并发编程过程中的记录总结。由于文章比较多,写的时间也比较散,所以我整理了个目录贴(传送门),方便查阅。

并发编程系列博客传送门


Thread类简介

Thread类是Java中实现多线程编程的基础类。本篇博客就来介绍下Thread类的常用API和常见用法。

Thread类常用的方法如下:

  • Thread.activeCount():这个方法用于返回当前线程的线程组中活动线程的数量,返回的值只是一个估计值,因为当此方法遍历内部数据结构时,线程数可能会动态更改。)。
  • Thread.checkAccess(): 检验当前正在执行的线程是否有权限修改thread的属性,这个方法我们一般不自己进行调用,Thread类的set方法在进行属性修改时都会先调用这个方法。
  • Thread.currentThread():获取当前正在运行的线程。
  • Thread.dumpStack():输出线程栈,一般在debug的时候调用。
  • Thread.enumerate(Thread tarray[]):??使用场景。
  • Thread.getAllStackTraces():获取系统中所有线程的线程栈信息。
  • thread.getName():获取线程的名字。
  • thread.getPriority():获取线程的优先级。
  • thread.getStackTrace():获取堆栈信息。
  • thread.getState():获取线程状态。
  • thread.getThreadGroup():获取线程所在线程组。
  • thread.interrupt():使得指定线程中断阻塞状态,并将阻塞标志位置为true。
  • thread.interrupted():测试当前线程是否被中断。
  • thread.isAlive():判断线程是否还存活着。
  • thread.isDaemon():判断线程是否是守护线程。
  • thread.join():在当前线程中加入指定线程,使得当前线程必须等待指定线程运行结束之后,才能结束。可以理解成线程插队、等待该线程终止。
  • Thread.sleep(long):强制线程睡眠一段时间。
  • thread.start():启动一个线程。
  • thread.setName(name):设置线程的名字。
  • thread.setPriority(priority):设置线程的优先级。
  • thread.setDaemon(true):将指定线程设置为守护线程。
  • thread.yield():使得当前线程退让出CPU资源,把CPU调度机会分配给同样线程优先级的线程。
  • object.wait()、object.notify()、object.notifyAll():Object类提供的线程等待和线程唤醒方法。

还有Thread类提供了功能丰富的构造函数,大家可以选合适的使用

示例代码


public class MyThread { public static void main(String[] args) {
Thread thread = Thread.currentThread(); //这个方法返回的是当前线程所在线程组以及这个线程组的子线程组内活动的线程数
//这个值是一个估计值,所以这个方法的应用场景不大
int activeCount = Thread.activeCount();
System.out.println("当前系统中活动线程数["+activeCount+"]"); //向标准错误输出流输出当前的线程栈,不会阻断程序的继续执行
Thread.dumpStack(); //获取所有线程栈信息
Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); //获取类加载器
ClassLoader contextClassLoader = thread.getContextClassLoader(); //获取当前线程名字
String threadName = thread.getName();
System.out.println("current thread name["+threadName+"]"); //获取当前线程ID
long threadId = thread.getId();
System.out.println("current thread id["+threadId+"]"); //获取当前线程的优先级,一共有1~10总共10个优先级,这个优先级并不是在
//所有平台都生效的
int priority = thread.getPriority();
System.out.println("current thread priority["+priority+"]"); StackTraceElement[] stackTrace = thread.getStackTrace();
System.out.println("-------------stackTrace info--------------");
for (int i = 0; i < stackTrace.length; i++) {
StackTraceElement element = stackTrace[i];
System.out.println("className:["+element.getClassName()+"]");
System.out.println("fileName:["+element.getFileName()+"]");
System.out.println("line nunber:["+element.getLineNumber()+"]");
System.out.println("method name:["+element.getMethodName()+"]");
System.out.println("is native method:["+element.isNativeMethod()+"]");
System.out.println("------------------------------------------");
} Thread.State state = thread.getState();
System.out.println("thread state:["+state+"]"); ThreadGroup threadGroup = thread.getThreadGroup();
String threadGroupName = threadGroup.getName();
System.out.println("thread group name:["+threadGroupName+"]"); //线程睡眠,调用sleep方法会使得线程进入timed_waiting状态,如果线程已经
//获得了锁资源,调用sleep方法是不会释放这个锁的
Thread.sleep(2000,500);
Thread.sleep(1000);
TimeUnit.SECONDS.sleep(2); Thread thread1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
TimeUnit.SECONDS.sleep(100);
}
});
thread1.start();
thread1.join(50); } }

守护线程

守护线程可以理解为服务线程,他们的作用就是服务于其他用户线程。当系统中不存在其他用户线程时,这些守护线程也会自动消亡。比如JVM的垃圾清理线程就是守护线程。我们可以使用如下方法查看和设置线程是否是守护线程。

thread.isDaemon();
thread.setDaemon(true);

sleep方法

Thread类中有一个静态的sleep方法,当一个执行中的线程调用了Thread的sleep方法后,调用线程会暂时让出指定时间的执行权,也就是在这期间不参与CPU的调度,但是该线程所拥有的监视器资源,比如锁还是持有不让出的。指定的睡眠时间到了后该函数会正常返回,线程就处于就绪状态,然后参与CPU的调度,获取到CPU资源后就可以继续运行了。如果在睡眠期间其他线程调用了该线程的interrupt()方法中断了该线程,则该线程会在调用sleep方法的地方抛出InterruptedException异常而返回(进入waiting状态线程的interrupt方法被调用,则这个线程会抛出InterruptedException异常)。

join方法

开发过程中我们可能会有这样的需求:多个线程分别加载资源,等这些线程资源加载完毕之后对这些资源做统一汇总处理。join方法就能实现类似的功能。

调用线程的join方法会使得调用线程进入waiting状态,直到被调用的线程执行结束,调用线程才会重新获得执行的机会。


public class MyThread { public static void main(String[] args) throws Exception { Thread thread1 = new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
TimeUnit.SECONDS.sleep(100);
}
});
thread1.start();
thread1.join();
System.out.println("main thread end...");
} }

上面的代码中,main线程调用了thread1的join方法,main线程会被挂起进入waiting状态,直到thread1执行完毕之后,main线程才有机会重新获得执行机会。另外,如果main线程的interrupt方法被其他线程调用,那么main线程调用的join方法会抛出InterruptedException异常。

join方法还有一个重载方法,这个方法可以指定超时时间。

thread1.join(50);

如果thread1线程在50ms内还没执行完,main线程就可以重新获得执行机会。

yeild方法

线程调用yield方法(这个方法是Thread的静态方法)是在暗示让这个线程让出CPU资源,如果这个线程在执行一个CPU时间,已经执行到一半了,调用yield之后这个线程会放弃剩下的一半CPU时间回到就绪状态。但是需要注意的是线程可以完全忽略yield方法的调用,也就是yield方法并不是每次都调用成功的:

  • 退让成功时,退让线程会由Running(运行)转为Runnable(就绪)状态。
  • 退让了的线程,与其他同优先级级别的线程一样,同样有再次获取CPU使用权的机会。

中断

先贴上一段网友对线程中断的总结。

  • 如果线程不处于阻塞状态,那么调用interrupt()方法仅仅是将[中断标志位]将被置为true;
  • 如果当前线程处于blocked阻塞(因为调用wait、sleep和join造成的)状态时被interrupt了,那么[中断标志位]将被清除,也就是设置为false,并且收到一个InterruptedException异常。
  • 如果当前线程处于blocked阻塞(因为NIO的InterruptibleChannel进行的I/O操作造成的)状态时被interrupt了,则会关闭channel,[中断标志位]将会被置为true,并且当前线程会收到一个ClosedByInterruptException异常。
  • 如果当前线程处于blocked阻塞(因为NIO的Selector造成的)状态时被interrupt了,那么[中断标志位]将被置为true,然后当前线程会立即从选择器区域返回并返回值(可能为非零的值)。

需要说明的是:interrupt()方法并不是中断线程,而是中断阻塞状态,也就是将线程的[中断标志位]置为true。中断后线程将继续执行。

PS:事实上,interrupt方法只是改变目标线程的中断状态(interrupt status),而那些会抛出InterruptedException异常的方法,如wait、sleep、join等,都是在方法内部不断地检查中断状态的值。

几个中断方法对比:

  • public static boolean interrupted():测试当前线程(这边要注意的是这个方法返回的是当前正在执行的线程的中断状态,注意和isInterrupted的区别)是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false。
  • public boolean isInterrupted():测试线程是否已经中断。线程的中断状态 不受该方法的影响,也就是说不清除中断标志。
public boolean isInterrupted() {
//传递false表示不清除标志位
return isInterrupted(false);
}
  • public void interrupt():中断线程,例如,当线程A运行时,线程B可以调用线程A的interrupt()方法来设置线程A的中断标志为true并立即返回。设置标志仅仅是设置标志,线程A实际并没有被中断,它会继续往下执行。如果线程A因为调用了wait系列函数、join方法或者sleep方法而被阻塞挂起,这时候若线程B调用线程A的interrupt()方法,线程A会在调用这些方法的地方抛出InterruptedException异常而返回。

当线程为了等待一些特定条件的到来时,一般会调用sleep函数、wait系列函数或者join()函数来阻塞挂起当前线程。比如一个线程调用了Thread. sleep(3000),那么调用线程会被阻塞,直到3s后才会从阻塞状态变为激活状态。但是有可能在3s内条件已被满足,如果一直等到3s后再返回有点浪费时间,这时候可以调用该线程的interrupt()方法,强制sleep方法抛出InterruptedException异常而返回,线程恢复到激活状态。

【并发编程】关于Thread类的详细介绍的更多相关文章

  1. Java并发编程:Thread类的使用介绍

    在学习Thread类之前,先介绍与线程相关知识:线程的几种状态.上下文切换,然后接着介绍Thread类中的方法的具体使用. 以下是本文的目录大纲: 一.线程的状态 二.上下文切换 三.Thread类中 ...

  2. Java并发编程:Thread类的使用

    Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知 ...

  3. 3、Java并发编程:Thread类的使用

    Java并发编程:Thread类的使用 在前面2篇文章分别讲到了线程和进程的由来.以及如何在Java中怎么创建线程和进程.今天我们来学习一下Thread类,在学习Thread类之前,先介绍与线程相关知 ...

  4. 【转】Java并发编程:Thread类的使用

    一.线程的状态 在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于对Thread类中的方法的理解. 线程从创建到最终的消亡,要经历若干个状态.一般来说,线程包括以 ...

  5. Java并发编程:Thread类的使用(转载)

    一:线程的状态: 在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类中的方法的理解. 线程从创建到最终的消亡,要经历若干个状态.一般来说,线程 ...

  6. 并发编程--Concurrent-工具类介绍

    并发编程--Concurrent-工具类介绍 并发编程--Concurrent-工具类介绍 CountDownLatch CylicBarrier Semaphore Condition 对象监视器下 ...

  7. Java 并发:学习Thread 类

    Java 中 Thread类 的各种操作与线程的生命周期密不可分,了解线程的生命周期有助于对Thread类中的各方法的理解.一般来说,线程从最初的创建到最终的消亡,要经历创建.就绪.运行.阻塞 和 消 ...

  8. 并发编程之 Thread 类过期方法和常用方法

    前言 在 Java 刚诞生时,Thread 类就已经有了很多方法,但这些方法由于一些原因(有一些明显的bug或者设计不合理)有些已经废弃了,但是他们的方法名却是非常的好,真的是浪费.我们在进行并发必编 ...

  9. Thread API的详细介绍

    import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurr ...

随机推荐

  1. [知识图谱]Neo4j知识图谱构建(neo4j-python-pandas-py2neo-v3)

    neo4j-python-pandas-py2neo-v3 利用pandas将excel中数据抽取,以三元组形式加载到neo4j数据库中构建相关知识图谱 Neo4j知识图谱构建 1.运行环境: pyt ...

  2. 网络安全-主动信息收集篇第二章-二层网络扫描之arping

    arping二层网络发现 介绍工具:arping arping主要查看IP的MAC地址 缺点:工具本身只能ping一个IP地址,不能ping一个IP段.但是可以通过脚本将整个网络中的IP进行扫描. 脚 ...

  3. CSP-S:追忆

    Warning:这一篇极其中二,开了那个大会莫名有感而发. 模拟测试17那套题啊... 开的这个大会为什么弄得我退役感如此强烈... 早就想收藏了,还是记下来吧 <入阵曲> 丹青千秋酿, ...

  4. [考试反思]1009csp-s模拟测试66:依旧

    依旧是好一场烂一场. 依旧是那么菜. 依旧是难止颓废. 依旧是在此方仰望,幻想? 上面这段中二的东西是为了防止Parisb说我的标题与内容无关而diss我莫名其妙115的语文. 但是菜是的确是菜... ...

  5. Spring Boot 2.x监控数据可视化(Actuator + Prometheus + Grafana手把手)

    TIPS 本文基于Spring Boot 2.1.4,理论支持Spring Boot 2.x所有版本 众所周知,Spring Boot有个子项目Spring Boot Actuator,它为应用提供了 ...

  6. 通俗易懂了解Vue双向绑定原理及实现

    看到一篇文章,觉得写得挺好的,拿过来给大家分享一下,刚好解答了一些困扰我的一些疑惑!!! 1. 前言 每当被问到Vue数据双向绑定原理的时候,大家可能都会脱口而出:Vue内部通过Object.defi ...

  7. go中的数据结构接口-interface

    1. 接口的基本使用 golang中的interface本身也是一种类型,它代表的是一个方法的集合.任何类型只要实现了接口中声明的所有方法,那么该类就实现了该接口.与其他语言不同,golang并不需要 ...

  8. PHP Laravel-包含你自己的帮助函数

    你可能想创建一个在应用的任何地方都可以访问的函数,这个教程将帮你实现

  9. 别翻了,这篇文章就是要让你入门java多线程!

    目录 1. 理解线程与进程 2.理解并行和并发 3.特殊的一个单线程:主线程(Main线程) 4. 创建多线程的四种方式 5.使用匿名内部类方式创建线程 6.线程安全问题 7.解决线程安全问题:线程同 ...

  10. nyoj 169-素数 (打表)

    169-素数 内存限制:64MB 时间限制:3000ms 特判: No 通过数:42 提交数:84 难度:1 题目描述: 走进世博园某信息通信馆,参观者将获得前所未有的尖端互动体验,一场充满创想和喜悦 ...