当前位置: 首页 > news >正文

Java多线程精讲:线程操作与状态转换全解析

前言        

      本章内容为作者结合学习与实践的总结整理,虽力求准确,但疏漏之处在所难免。若有任何疑问或建议,恳请读者朋友们不吝指正,共同完善知识体系,感激不尽!


一、认识多线程(Thread)

1、什么是线程

        一个线程就是一个 "执行流",每个线程之间都可以按照顺序执行自己的代码,多个线程之间 "同时" 执行着多份代码

        在之前我们写的Java代码中,都必然包含一个main()方法,对于单线程来讲,一个程序的执行从 main() 方法开始,直到 main()  方法执行结束,这其中就是一个线程的开始到结束。也可以说,在单线程程序中,main线程是程序的入口和主线程

2、线程的作用

        线程是计算机中处理任务的基本单位,它可以让程序同时执行多个任务,提高程序的并发性和性能

        提高程序的响应速度:多线程可以让程序同时执行多个任务,避免某一个任务的阻塞影响整个程序的运行速度,从而提高程序的响应速度

        提高计算机的利用率:多线程可以充分利用多核处理器的优势,同时处理多个任务,提高计算机的资源利用率。

        实现并发编程:线程可以让程序在同一时间执行多个任务,实现并发编程,使程序更加灵活、高效

        支持异步编程:线程可以用于实现异步编程,让程序在执行耗时操作时可以继续执行其他任务,提高程序的效率和用户体验

3、进程和线程的区别

        进程是操作系统分配资源的基本单位

3.1、进程和线程的区别

        (1)进程是包含线程的,每个进程至少包含一个线程,即主线程

        (2)进程和进程之间不共享一个内存空间,拥有各自独立的内存空间和系统资源,而同一个进程下的线程之间共享一个内存空间

        (3)进程是操作系统中进行资源分配的最小单位,而线程是进程内的执行单元,是操作系统进行调度的最小单位

        (4)进程之间互相独立,当一个进程崩溃时,不会影响其他进程,但是如果一个线程崩溃了,可能会导致整个进程崩溃

4、Java中的线程

        Java 标准库中的 Thread 类可以看作是对操作系统提供的多线程 API 进行了更高级的抽象和封装

二、创建线程

1、继承Thread类

        继承Thread来创建一个线程类

package Thread;
class MyThread extends Thread{
    public void run() {
        System.out.println("这是 Thread 线程");
    }
}
public class Demo1{
    public static void main (String[] args){
        Thread t=new MyThread();
        t.start();
        System.out.println("这是 Main 线程");
    }
}

2、实现Runnable接口

  定义一个MyRunnable类,实现Runnable接口,重写run方法,在main函数中定义一个MyRunnable对象,传人Thread的构造方法中,最后调用start方法启动线程。

class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("实现Runnable接口");
    }
}
public class Demo2 {
    public static void main(String[] args){
        Runnable runnable=new MyRunnable();
        Thread t=new Thread(runnable);
        t.start();
        System.out.println("主线程结束");
    }
}

3、其他方法

        分别是匿名内部类创建Thread子类对象 匿名内部类创建Runnable子类对象Lambda表达式创建Runnable子类对象

//匿名内部类创建Thread子类对象
public class Demo3 {
    public static void main(String[] args){
        Thread t=new Thread(){
            public void run() {
                System.out.println("Hello Thread");
            }
        };
        t.start();
        System.out.println("Hello Main");
    }
}
//匿名内部类创建Runnable子类对象
public class Demo4 {
    public static void main(String[] args){
        Runnable runnable=new Runnable() {
            public void run() {
                System.out.println("Hello Runnable");
            }
        };
        Thread t=new Thread(runnable);
        t.start();
        System.out.println("Hello Main");
    }
}
//Lambda表达式创建Runnable子类对象
public class Demo5 {
    public static void main(String[] args){
        Thread t=new Thread(()->{
            System.out.println("Hello Thread");
        });
        t.start();
        System.out.println("Hello Main");
    }
}

三、Thread类中的方法和属性

1、常见的构造方法

        为线程指定名称是为了方便我们以后调试,创建一个名字为Aokey的线程 ,每休眠两秒打印一个Aokey,该线程会一直执行

public class Demo7 {
    public static void main(String[] args) {
        Thread t1=new Thread(() -> {
            while(true){
                try {
                    Thread.sleep(2000);
                    System.out.println("Aokey");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"Aokey");
        t1.start();
    }
}

 我们打开位于

        此路径下的 jconsole.exe 程序,可看到下图出现名称为Aokey的线程,除这个线程以外,其他系统线程是由JVM(Java虚拟机)自动创建和管理的,用于支持Java程序的运行和管理。

        那么我们之前提到的main线程,即主线程哪去了,答案是已经技术了,我们能够看到Aokey线程运行是因为我们上述代码在该线程中是一个死循环,所以一直在运行,当然如果我们创建线程不命名的话,在运行时我们看到的是Thread-0、Thread-1等。

 2、常见属性

这些方法通常用于在编程中访问和操作线程对象的属性。例如,

        getId() 方法返回线程的唯一标识符

        getName() 方法返回线程的名称

        getState() 方法返回线程的当前状态(如NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)

        getPriority() 方法返回线程的优先级

        isDaemon() 方法判断线程是否为守护线程(后台线程)

        isAlive() 方法判断线程是否仍然存活

        isInterrupted() 方法判断线程是否被中断。 

注意:JVM会在一个进程的所有非后台线程结束后,才会结束运行。 

3、启动线程——start( )

 

 

        我们重写的run()方法创建线程对象,并不意味着线程就开始运行了 ,实际上,run()方法是线程的执行体,定义了线程需要执行的任务。线程的执行是通过调用start()方法来启动的

        注意:每个线程只能 start 一次

4、中断线程——interrupt( )

public class Demo8 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(() -> {
            while(!Thread.interrupted()){
                try {
                    Thread.sleep(2000);
                    System.out.println("Aokey");
                } catch (InterruptedException e) {
                    break;
                }
            }
        },"Aokey");
        t1.start();
        Thread.sleep(6000);
        System.out.println("中断线程");
        t1.interrupt();
    }
}

        在线程t1的循环中,通过调用Thread.interrupted()方法来检查线程是否被中断,如果线程被中断,则会抛出InterruptedException异常,通过捕获该异常并跳出循环来结束线程t1的运行。 

5、等待一个线程——join( ) 

        在多线程编程中,可以使用join()方法来等待一个线程执行结束。当调用一个线程的join()方法时,当前线程会被阻塞,直到被调用的线程执行结束。

public class Demo9 {
    private static int count=0;
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(()->{
            System.out.println("t线程已开始");
           while(true) {
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               count++;
               System.out.println("t线程执行了"+count+"次");
           }
        });
        t.start();
        t.join();
        System.out.println("main线程结束");
    }
}

         上文我们查看 Aokey线程 时,发现 主线程 早已结束,如果我们调用 t线程 的 join()方法 时,主线程 会被阻塞,直到 t线程 结束

6、获取当前线程引用——currentThread( )

        尝试获取main方法中的当前线程,并打印该线程的名称 

public class Demo10 {
    public static void main(String[] args) {
        Thread t=Thread.currentThread();
        System.out.println(t.getName());
    }
}

运行结果: 

 7、线程休眠——sleep( )

四、线程状态 

图片来源于网络: 

  • NEW:表示已经安排了工作,但尚未开始执行。
  • RUNNABLE:表示线程可以运行,包括正在工作中和即将开始工作的状态。
  • BLOCKED:表示线程被阻塞,需要等待其他事件或资源释放。
  • WAITING:表示线程正在等待某个条件满足,例如锁的释放或其他线程的通知。
  • TIMED_WAITING:与WAITING类似,但设置了超时时间,如果在规定时间内条件未满足,线程将继续执行。
  • TERMINATED:表示线程已经完成了所有任务并终止。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dtcms.com/a/89644.html

相关文章:

  • Mycat安装验证流程整理
  • 深度学习|表示学习|多头注意力在计算时常见的张量维度变换总结|28
  • C盘急救实录:从爆红到畅快
  • 量子计算模拟中的测量与噪声建模:基于 3 量子比特系统分析
  • AI-Sphere-Butler之Ubuntu服务器如何部署Nginx代理,并将HTTP升级成HTTPS,用于移动设备访问
  • 余弦退火算法与学习率预热
  • Apache HttpClient使用
  • 内核编程十二:打印task_struct中的数据
  • 高性能C#定时删除图片,包含定时触发、分批删除、异步处理和资源监控
  • JavaScript基础-常用的鼠标事件
  • 卷积神经网络的原理、实现及变体
  • c语言基础编程入门练习题(三)
  • 工欲善其事必先利其器————idea插件
  • 11_JavaScript_字符串方法+数学方法
  • react中防止数据多大并需要二次加工处理进行单线程转多线程webworker优化处理(不借助react-webworker)
  • Python Sanic面试题及参考答案
  • 东软鸿蒙C++开发面经
  • 网络华为HCIA+HCIP 防火墙
  • VLAN综合实验实验报告
  • git的进阶使用
  • Spring Boot JSON序列化深度管控:忽略指定字段+Jackson扩展策略破解双向实体循环引用问题
  • Linux -- 进程间通信(IPC)-- 进程间通信、管道、system V 共享内存、system V 消息队列、责任链模式 、system V 信号量
  • AI与数据的双向奔“赋”
  • 超融合服务器与普通服务器的具体区别
  • 226.翻转二叉树
  • ubuntu20.04 修改输入法设置后 界面卡死终端乱码 解决方法
  • 23中设计模式-迭代器(Iterator)设计模式
  • Netty源码—Pipeline和Handler(二)
  • Day39 | 724. 寻找数组的中心下标、34. 在排序数组中查找元素的第一个和最后一个位置、922. 按奇偶排序数组 II、35. 搜索插入位置
  • 如何用腾讯云建站做好一个多语言的建筑工程网站?海外用户访问量提升3倍!分享我的经验