多线程编程之线程常用的API大盘点【笔面试必备】

简介: 之前的两篇文章讲解了一些关于线程的相关概念和知识点,线程这块内容在平时的开发或者找工作的面试中也是常常被问起的知识点。之前写文章也是比较乱,想到什么写什么,没有一定的组织性,所以准备后面以系列文章的方式来记录和总结一些关于线程的知识点。我自己也是个学习者,通过写文章的方式来加强自己对于知识点的掌握和理解,所以如果有一些不正确或者不全的地方,欢迎大家指正。

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();
        }
    }
}
复制代码


942206831a604536bb73c55a73bdf709~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


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方法结束:");
    }
}
复制代码


fbec04f7b72b4a258e068da4fb381bc5~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


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());
    }
}
复制代码


b0a5e44464d1426890987d244a77ecb6~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


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方法结束");
    }
}
复制代码


d294d431ecbd4174a03e4dfa018a7a4a~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


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());
    }
}
复制代码


           d294d431ecbd4174a03e4dfa018a7a4a~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


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());
    }
}
复制代码


72d9523b6a9540029c79eb0c2d016853~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


注:高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部执行完。当线程优先级的等级差距很大时,谁先执行完和代码的调用顺序无关。


线程的优先还有『随机性』,也就是说优先级高的线不一定每一次都先执行完成。


比如:


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)+"毫秒");
    }
}
复制代码


93c13a0ff4ae4f518ff324c1c2bc1aa1~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


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();
            }
        }
    }
}
复制代码


d47505a7c42d477c8db6d3171f6d1565~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


注意:


1、thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。不能把正在运行的常规线程设置为守护线程。


2、在Daemon线程中产生的新线程也是Daemon的。


3、守护线程不能用于去访问固有资源,比如读写操作或者计算逻辑。因为它会在任何时候甚至在一个操作的中间发生中断。


4、Java自带的多线程框架,比如ExecutorService,会将守护线程转换为用户线程,所以如果要使用后台线程就不能用Java的线程池。


03 总结


以上就是关于线程的一些常用的API的总结,如果你觉得写的不错或者对你有用,就分享给更多的人呗。


后面将继续分享关于多线程编程的一些其他的知识点。


目录
相关文章
|
11天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
39 2
|
10天前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
|
30天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
20 3
|
30天前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
19 2
|
30天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
30 2
|
30天前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
25 1
|
1月前
|
监控 安全 算法
线程死循环确实是多线程编程中的一个常见问题,在编码阶段规避潜在风险
【10月更文挑战第12天】线程死循环确实是多线程编程中的一个常见问题,在编码阶段规避潜在风险
47 2
|
2月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
1月前
|
监控 安全 算法
线程死循环确实是多线程编程中的一个常见问题,它可能导致应用程序性能下降,甚至使整个系统变得不稳定。
线程死循环是多线程编程中常见的问题,可能导致性能下降或系统不稳定。通过代码审查、静态分析、日志监控、设置超时、使用锁机制、测试、选择线程安全的数据结构、限制线程数、使用现代并发库及培训,可有效预防和解决死循环问题。
61 1
|
1月前
|
监控 安全 算法
线程死循环是多线程编程中的常见问题,可能导致应用性能下降甚至系统不稳定。
【10月更文挑战第6天】线程死循环是多线程编程中的常见问题,可能导致应用性能下降甚至系统不稳定。为了解决这一问题,可以通过代码审查、静态分析、添加日志监控、设置超时机制、使用锁和同步机制、进行全面测试、选用线程安全的数据结构、限制线程数量、利用现代并发库,并对团队进行培训等方法来预防和减少死循环的发生。尽管如此,多线程编程的复杂性仍需要持续监控和维护以确保系统稳定。
52 3
下一篇
无影云桌面