01 前言
之前的两篇文章讲解了一些关于线程的相关概念和知识点,线程这块内容在平时的开发或者找工作的面试中也是常常被问起的知识点。之前写文章也是比较乱,想到什么写什么,没有一定的组织性,所以准备后面以系列文章的方式来记录和总结一些关于线程的知识点。我自己也是个学习者,通过写文章的方式来加强自己对于知识点的掌握和理解,所以如果有一些不正确或者不全的地方,欢迎大家指正。
之前的文章传送门:线程以及线程的常用方法;线程的六种状态;Java并发编程之Volatile关键字解析。
关于线程的基本介绍以及线程的创建和启动前面的文章已经写过了,所以这篇文章准备总结以下关于线程的一些常用的API。
线程常用的API也有很多,这里总结一些比较常用的方法。
02 正文
1、sleep方法
sleep()是静态方法,所以最好的调用方法就是 Thread.sleep()。常用格式如下:
sleep(long millis) :线程睡眠 millis 毫秒
sleep(long millis, int nanos):线程睡眠 millis 毫秒 + nanos 纳秒
线程的sleep方法应该写在线程的run()方法里,就能让对应的线程睡眠指定的时间。
代码如下:
package com.jiangxia.chap1; /** * thread的sleep方法 * author:jiangxia * date:2021-04-14 */ public class Demo01 { public static void main(String[] args) { Thread t = new Thread1(); System.out.println("开始时间"+System.currentTimeMillis()); //t.start方法启动线程 t.start(); System.out.println("结束时间"+System.currentTimeMillis()); } } /** * 继承Thread类的方式实现线程 */ class Thread1 extends Thread{ @Override public void run() { /** * 线程休眠10秒 */ try { System.out.println("线程开始时间:"+System.currentTimeMillis()); Thread.sleep(10000); System.out.println("线程结束时间"+System.currentTimeMillis()); } catch (InterruptedException e) { e.printStackTrace(); } } } 复制代码
2、currentThread方法
currentThread()方法可以返回段正在被哪个线程调用的信息。Thread.currentThread()的引用对象和this引用对象不同,Thread.currendThread()获取的事使用该方法的线程对象,this获取的是当前对象。
代码如下:
package com.jiangxia.chap1; /** * 线程的currentThread方法 * author:jiangxia * date:2021-04-14 */ public class Demo02 { public static void main(String[] args) { Thread t = new Demo8Thread(); t.start(); System.out.println("main方法:"+Thread.currentThread().getName()); } } class Demo8Thread extends Thread{ public Demo8Thread(){ System.out.println("构造方法开始:"); System.out.println("构造方法:"+Thread.currentThread().getName()); System.out.println(this.getName()); System.out.println("构造方法结束:'"); } @Override public void run() { System.out.println("run方法开始:"); System.out.println("run方法:"+Thread.currentThread().getName()); System.out.println(this.getName()); System.out.println("run方法结束:"); } } 复制代码
3、getName()、getId()
Thread.getName() 方法返回当前线程的名字。getId的作用则是获取当前线程的唯一标识。
package com.jiangxia.chap1; /** * 线程的getName和getId * author:jiangxia * date:2021-04-14 */ public class Demo03 { public static void main(String[] args) { Thread thread = Thread.currentThread(); System.out.println("线程的Name:"+thread.getName()); System.out.println("线程的ID:"+thread.getId()); } } 复制代码
4、isAlive方法
方法isAlive() 的功能是判断当前的线程是否处于活动状态;活动状态就是线程已经启动尚未终止,那么这时候线程就是存活的,则返回true,否则则返回false。
代码如下:
package com.jiangxia.chap1; /** * isAlive方法 * author:jiangxia * date:2021-04-14 */ public class Demo9 { public static void main(String[] args) { Thread t = new Demo9Thread(); System.out.println("准备启动线程:"+t.isAlive()); t.start();//启动线程 System.out.println("线程已启动:"+t.isAlive()); } } class Demo9Thread extends Thread{ public Demo9Thread(){ System.out.println("构造方法开始"); //Thread.currentThread().isAlive() 主线程的状态 System.out.println(Thread.currentThread().isAlive()); System.out.println(this.isAlive()); System.out.println("构造方法结束"); } @Override public void run() { System.out.println("run方法开始"); //当前线程的状态 System.out.println(Thread.currentThread().isAlive()); System.out.println("run方法的运行状态:"+this.isAlive()); System.out.println("run方法结束"); } } 复制代码
5、getState方法
getState() 方法返回该线程的状态。线程主要有以下几种状态:
状态 | 描述 |
NEW | 尚未启动的线程 |
RUNNABLE | 在Java虚拟机中正在执行的线程 |
BLOCKED | 补阻塞等待对象锁的线程 |
WAITING | 正在等待另一个线程执行特定动作的线程 |
TIME_WAITING | 正在等待另一个线程执行到指定等待时间的线程 |
TERMINATED | 已经退出运行的线程 |
package com.jiangxia.chap1; /** * 线程的状态 * author:jiangxia * date:2021-04-14 */ public class Demo1 { public static void main(String[] args) throws InterruptedException { Thread t = new Demo1Thread(); System.out.println("线程在main方法的状态A:"+t.getState()); Thread.sleep(2000); t.start(); Thread.sleep(2000); System.out.println("线程在main方法的状态B:"+t.getState()); } } class Demo1Thread extends Thread{ public Demo1Thread(){ System.out.println("构造方法的状态:"+Thread.currentThread().getState()); } @Override public void run() { System.out.println("Run方法的状态:"+Thread.currentThread().getState()); } } 复制代码
6、yield方法
yield方法的作用是放弃当前的CPU资源,将资源让给其它的任务去占用CPU执行。但放弃时间不确定,有可能刚刚放弃,马上又获取CPU时间片。
代码如下:
package com.jiangxia.chap1; /** * yield方法 * author:jiangxia * date:2021-04-14 */ public class Demo23 { public static void main(String[] args) { Thread t1 = new Demo23Thread(); t1.start(); } } class Demo23Thread extends Thread{ @Override public void run() { int count =0; long starttime = System.currentTimeMillis(); for (int i = 0; i < 100000 ; i++) { yield(); count+=i; } long endtime = System.currentTimeMillis(); System.out.println("计算总共用时:"+(endtime-starttime)+"毫秒"); } } 复制代码
复制代码
7、getPriority方法、setPriority方法获取和set线程的优先级
在操作系统中,线程可以划分优先级,优先级较高的线程得到更多的CPU资源,也就CPU会优先执行优先级较高的线程对象中的任务。设置线程优先有助于帮助『线程调度器』确定在下一次选择哪个线程优先执行。
设置线程的优先级使用setPriority方法,优级分为1~10个级别,如果设置优先级小于1或大于10,JDK抛出
IllegalArgumentException。JDK默认设置3个优先级常量,MIN_PRIORITY=1(最小值),NORM_PRIORITY=5(中间值,默认),MAX_PRIORITY=10(最大值)。
获取线程的优先级使用getPriority方法。设置线程的优先级使用setPriority方法。
package com.jiangxia.chap1; /** * getPriority、setPriority * author;jiangxia * date:2021-04-14 */ public class Demo24 { public static void main(String[] args) { //默认的优先级是5 System.out.println("主线程的运行优先级:"+Thread.currentThread().getPriority()); Thread.currentThread().setPriority(9); System.out.println("使用setPriority()方法设置优先级后的主线程的优先级:"+Thread.currentThread().getPriority()); Thread t = new Thread24(); t.start(); } } class Thread24 extends Thread{ @Override public void run() { //由于线程的优先级具有继承性,所以在主线程使用了Thread.currentThread().setPriority(9)后,该线程的优先级也是9 System.out.println("线程的优先级:"+this.getPriority()); } } 复制代码
注:高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部执行完。当线程优先级的等级差距很大时,谁先执行完和代码的调用顺序无关。
线程的优先还有『随机性』,也就是说优先级高的线不一定每一次都先执行完成。
比如:
package com.jiangxia.chap1; import java.util.Random; /** * 线程优先级 * author :jiangxia * date:2021-04-14 */ public class Demo25 { public static void main(String[] args) { for (int i = 0; i <10 ; i++) { Thread t1 = new Thread25(); t1.setName("A"+i); //t1线程设置最高级别 t1.setPriority(Thread.MAX_PRIORITY); t1.start(); Thread t2 = new Thread25(); t2.setName("B"+i); //t2线程设置最低级别 t2.setPriority(Thread.MIN_PRIORITY); t2.start(); } } } class Thread25 extends Thread{ @Override public void run() { long start = System.currentTimeMillis(); long count = 0; for (int i = 0; i < 10; i++) { for (int j = 0; j <50000 ; j++) { Random r = new Random(); count = i*j + r.nextInt(); } } long end = System.currentTimeMillis(); System.out.println("线程"+this.getName()+"执行完成使用了"+(end-start)+"毫秒"); } } 复制代码
8、setDaemon设置守护线程
在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
守护线程是一种特殊的线程,特殊指的是当进程中不存在用户线程时,守护线程会自动销毁。典型的守护线程的例子就是垃圾回收线程,当进程中没有用户线程,垃圾回收线程就没有存在的必要了,会自动销毁。
可以理解为:任何一个守护线程都是整个JVM中所有非守护线程的保姆。只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器)。
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。
setDaemon方法可以将指定的线程设置为守护线程。比如:
package com.jiangxia.chap1; /** * 守护线程 * author:jiangxia * date:2021-04-15 */ public class Demo26 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread26(); thread.setDaemon(true);//把指定线程设置为守护线程 thread.start(); //IllegalThreadStateException 错误信息 //thread.setDaemon(true);//把指定线程设置为守护线程 Thread.sleep(5000); System.out.println("主线程结束"); } } class Thread26 extends Thread{ @Override public void run() { while(true){ try { System.out.println("当前时间:"+System.currentTimeMillis()); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } 复制代码
注意:
1、thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。不能把正在运行的常规线程设置为守护线程。
2、在Daemon线程中产生的新线程也是Daemon的。
3、守护线程不能用于去访问固有资源,比如读写操作或者计算逻辑。因为它会在任何时候甚至在一个操作的中间发生中断。
4、Java自带的多线程框架,比如ExecutorService,会将守护线程转换为用户线程,所以如果要使用后台线程就不能用Java的线程池。
03 总结
以上就是关于线程的一些常用的API的总结,如果你觉得写的不错或者对你有用,就分享给更多的人呗。
后面将继续分享关于多线程编程的一些其他的知识点。