【JAVAEE】多线程

【JAVAEE】多线程

  • 一、进程
      • [1.1 进程的定义](#1.1 进程的定义)
      • [1.2 进程和线程的联系](#1.2 进程和线程的联系)
  • 二、线程
      • [2.1 JConsole工具](#2.1 JConsole工具)
      • [2.2 创建线程](#2.2 创建线程)
        • [2.2.1 Thread类,start(),run()](#2.2.1 Thread类,start(),run())
        • [2.2.2 继承Thread类](#2.2.2 继承Thread类)
        • [2.2.3 实现Runnable接口](#2.2.3 实现Runnable接口)
        • [2.2.4 匿名内部类](#2.2.4 匿名内部类)
        • [2.2.5 使用Runnable接口的匿名内部类](#2.2.5 使用Runnable接口的匿名内部类)
        • [2.2.6 使用lambda表达式](#2.2.6 使用lambda表达式)
      • [2.3 Thread类及常用方法](#2.3 Thread类及常用方法)
        • [2.3.1 Thread类的构造方法](#2.3.1 Thread类的构造方法)
        • [2.3.2 Thread类的常见属性](#2.3.2 Thread类的常见属性)
      • [2.4 线程状态](#2.4 线程状态)
      • [2.5 后台线程和前台线程](#2.5 后台线程和前台线程)
      • [2.6 休眠线程](#2.6 休眠线程)
      • [2.7 线程等待](#2.7 线程等待)
      • [2.8 终止线程](#2.8 终止线程)
        • [2.8.1 自定义标志位终止线程](#2.8.1 自定义标志位终止线程)
        • [2.8.2 使用自带标志位终止线程](#2.8.2 使用自带标志位终止线程)

博客结尾包含此篇博客的全部代码!!!

一、进程

1.1 进程的定义

  • 进程(Process)是计算机操作系统中的一个核心概念,它是程序在计算机上的一次动态执行实例。换句话说,进程是程序运行时的活动表现形式。它包含了程序代码、数据、运行状态以及系统资源的分配情况。进程相当于一个正在运行的程序
  • 进程也是系统分配资源的基本单位

1.2 进程和线程的联系

首先,并发编程成为我们的需求!每个客户端给服务器发送请求,服务器就需要给客户端提供服务。在90年代,多进程是一种解决方案!!!

那么既然有解决方法,为什么还要引入线程?

线程:线程是轻量级的进程!!!

线程相比于进程:

  • 创建线程比创建进程更快。
  • 销毁线程比销毁进程更快。
  • 调度线程比调度进程更快。

举个例子:

假设这个任务是"消灭"桌子上的两只鸡,在一个房间中有一个桌子,桌子上放了两只鸡。

进程的解决方法:再创建一个房间,将这两只鸡分开,分别"消灭"。

线程的解决方法:在这个房间中"消灭"这两只鸡。

这两个对比你发现:相同的人数,进程需要再创建一个房间(开销很大),而线程则啥都不需要添加。

二、线程

2.1 JConsole工具

在创建线程之前,先给大家介绍一下JConsole 工具, JConsole是JDK 自带的一款图形化监控和管理工具,基于 JMX(Java Management Extensions)技术实现。它能够连接到本地或远程的 Java 虚拟机(JVM),并实时监控和管理 Java 应用程序的性能和资源使用情况。

这里我们就用它来监控我们的线程!

JConsole:找到我们的JDK安装的安装路径,像我这里安装的JDK17-->bin-->jconsole.exe

如何使用?

测试代码:代码运行起来才能观察到,如果线程结束就观察不到。

java 复制代码
class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println(" hello Thread");
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
    }
}

刚进去出现不安全连接,由于是我是自己练习,所以点不安全连接。

2.2 创建线程

2.2.1 Thread类,start(),run()
  • Thread类:线程,是操作系统的概念,操作系统定义了一些api(应用程序编程接口)来供程序员使用,而java中将这些api封装成Thread类来供使用!Thread类在java.long这个包中,所以用的时候不需要导包。
  • start():用于启动一个新线程,线程调度器会调用 run() 方法。start() 方法只能被调用一次。如果尝试多次调用 start(),会抛出 IllegalThreadStateException 异常。
  • run():定义线程的执行逻辑,但不会启动新线程。如果需要启动新线程,必须通过 start() 方法。相当于线程的入口。
2.2.2 继承Thread类

代码:

java 复制代码
class MyThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println(" hello Thread");
        }
    }
}

public class Demo1 {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.start();
        while(true) {
            System.out.println(" hello main");
        }
    }
}
2.2.3 实现Runnable接口

实现Runnable接口的MyRunnnable类,将new MyRunnable()当作一个对象传给Thread的构造函数。

代码:

java 复制代码
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Hello Thread");
    }
}

public class Demo2 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyRunnable());
        t1.start();
    }
}
2.2.4 匿名内部类
java 复制代码
public class Demo3 {
    public static void main(String[] args) {
        Thread thread = new Thread(){
            @Override
            public void run() {
                System.out.println("Hello Thread");
            }
        };

        thread.start();
        System.out.println("Hello main");
    }
}
2.2.5 使用Runnable接口的匿名内部类
java 复制代码
public class Demo4 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable(){
        @Override
            public void run() {
                System.out.println("Hello Thread");
            }
        });
        
        t1.start();
    }
}
2.2.6 使用lambda表达式
java 复制代码
public class Demo5 {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            while(true){
                System.out.println("Hello Thread");
            }
        });
        thread.start();
        while(true){
            System.out.println("Hello main");
        }
    }
}

2.3 Thread类及常用方法

2.3.1 Thread类的构造方法

解释一下第四个Thread(Runnable target,String name)!

java 复制代码
public class Demo6 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("Hello Thread");
                }
            }
        }, "Thread1");
        thread.start();
        while (true) {
            System.out.println("Hello main");
        }
    }
}
2.3.2 Thread类的常见属性

• ID 是线程的唯⼀标识,不同线程不会重复

• 获取线程的名称

• 获取线程的当前状态,如新建、就绪、运行、阻塞、等待、终止等

• 优先级⾼的线程理论上来说更容易被调度到

• 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。

• 是否存活,即简单的理解,为 run ⽅法是否运⾏结束了

• 判断线程是否被中断。线程的中断状态可以通过调用 interrupt() 方法来设置,该方法会改变线程的中断状态,但不会停止线程的执行

2.4 线程状态

java 复制代码
public class Demo7 {
    public static void main(String[] args) {
        for(Thread.State state: Thread.State.values()) {
            System.out.println(state);
        }
    }
}

获取线程的所有状态:

• NEW:

  • 当线程对象被创建,但尚未调用 start() 方法时,线程处于新建状态。
  • 在这个状态下,线程尚未开始执行。
java 复制代码
public class Demo7 {
    public static void main(String[] args) {
        Thread thread=new Thread(()->{
            System.out.println("Hello Thread");
        });

        System.out.println(thread.getState());
        thread.start();

    }
}

• RUNNABLE:

  • 当调用线程的 start() 方法后,线程进入就绪状态。
  • 在这个状态下,线程已经准备好运行,等待 CPU 时间片以便执行。
  • 就绪状态的线程可能正在 JVM 中运行,也可能正在等待操作系统调度。
java 复制代码
public class Demo7 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            for(int i=0;i<3;i++) {
                System.out.println("Hello Thread");
            }
        });

        System.out.println(thread.getState());
        thread.start();
        System.out.println(thread.getState());
    }
}

• BLOCKED: 由锁导致的,后面会出一篇关于线程安全问题,到时候会详细介绍,这里就不过多介绍了

  • 当线程等待获取一个排他锁,如同步块或同步方法中的锁时,线程进入阻塞状态。
  • 线程在等待锁释放后才能继续执行。
  • 阻塞状态的线程不会被分配 CPU 时间片。

• WAITING:

时间阻塞(这个等待是没时间上限的)

  • 当线程执行 wait()、join() 或 LockSupport.park() 方法后,线程进入等待状态。
  • 在这个状态下,线程需要等待其他线程执行特定的操作(如通知或中断)才能继续。
  • 等待状态的线程不会被分配 CPU 时间片。
java 复制代码
public class Demo7 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(true) {

            }
        });

        System.out.println(thread.getState());
        thread.start();
        thread.join();
    }
}

发现main线程是waiting。

• TIMED_WAITING:

时间阻塞(这个等待是有时间上限的),不参与CPU调度

java 复制代码
public class Demo7 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            while(true) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        System.out.println(thread.getState());
        thread.start();
        Thread.sleep(1000);
        System.out.println(thread.getState());
    }
}

• TERMINATED:

  • 当线程的 run() 方法执行完毕,或者线程被 stop() 方法(已废弃)强制停止,或者线程抛出未捕获的异常导致运行结束时,线程进入终止状态。
  • 在这个状态下,线程已经结束执行,不再消耗任何资源。
java 复制代码
public class Demo7 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            for(int i=0;i<3;i++) {
                System.out.println("Hello Thread");
            }
        });

        System.out.println(thread.getState());
        thread.start();
        Thread.sleep(6000);
        System.out.println(thread.getState());
    }
}

2.5 后台线程和前台线程

前台线程:一般我们创建的线程都是前台线程,只要前台线程不结束,JVM就不会退出。

后台线程:也称守护线程,后台线程一般都是辅助性的任务,如果前台线程全部结束,即使还有后台线程在运行,JAM也会退出。

java 复制代码
public class Demo8 {

    public static void main(String[] args) {
        Thread thread1=new Thread(()->{
            for(int i=0;i<2;i++){
                System.out.println("Hello Thread");
            }
        });

        thread1.start();
        System.out.println(thread1.isDaemon());//false 说明他不是守护线程

        Thread thread2=new Thread(()->{
            for(int i=0;i<2;i++){
                System.out.println("Hello Thread");
            }
        });
        thread2.setDaemon(true);
        thread2.start();

        System.out.println(thread2.isDaemon());//true
    }
}

2.6 休眠线程

下面那个可以达到纳秒级别,这种适用于军工,航天等之类,一把情况下用不到。

这里还有一个需要注意的是:由于 Runnable 接口的 run 方法没有 throws 子句,所以这里只能用try-catch 来捕获异常。

2.7 线程等待

java 复制代码
public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 3; i++){
                System.out.println("Hello Thread");
            }
        });
        thread.start();
        thread.join();
        System.out.println("Hello main");
    }
}

先执行thread线程,thread线程执行完,再执行main线程。为了防止死等,也可以设置等待时间。

2.8 终止线程

让run方法尽快结束。

2.8.1 自定义标志位终止线程
java 复制代码
public class Demo11 {
    public static  boolean flag=true;
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            while(flag){
                System.out.println("Hello Thread");
            }
            System.out.println("Bye Bye Thread");
        });
        thread.start();
        Thread.sleep(1000);
        flag=false;
    }
}

将flag设置为全局变量,这里就是匿名内部类访问外部类,而不是变量捕获。

为什么不可以将flag设置为局部变量?

很有可能是main线程已经执行完,已经将flag销毁了,但是Thread线程中还没执行到while(flag)...

2.8.2 使用自带标志位终止线程

Java自带标志位来结束终止线程。先使用Thread.currentThread()来获取当前线程,在.isInterrupted()获取标志位。然后再主进程中调用interrupte()方法来将标志位值修改为true。

java 复制代码
public class Demo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (!Thread.currentThread().interrupted()) {
                System.out.println("Hello Thread");
            }
            System.out.println("Bye Bye Thread");
        });
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

调用interrupte()方法,不仅会设置标志位,还会提前唤起sleep阻塞

java 复制代码
public class Demo12 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() ->{
            while (!Thread.currentThread().isInterrupted()) {
                System.out.println("hahah");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    //1.不操作继续执行线程(因为sleep唤醒后会又将标志位改为true)
                   e.printStackTrace();

                    //2.结束线程
                    break;
                    //3.进行其它操作
                }
            }
        });
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

此篇博客的全部代码!!!

相关推荐
计算机-秋大田1 小时前
基于Spring Boot的乡村养老服务管理系统设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
盖盖衍上1 小时前
Java 泛型(Generics)详解与使用
java·开发语言·windows
没有十八岁2 小时前
云创智城YunCharge 新能源二轮、四轮充电解决方案(云快充、万马爱充、中电联、OCPP1.6J等多个私有单车、汽车充电协议)之新能源充电行业系统说明书
java·数据库·spring·汽车
小萌新上大分3 小时前
Minio搭建并在SpringBoot中使用完成用户头像的上传
java·spring boot·后端·minio·minio搭建·头像上传·minio入门
B站计算机毕业设计超人3 小时前
计算机毕业设计SpringBoot+Vue.js校园失物招领系统(源码+文档+PPT+讲解)
java·vue.js·spring boot·后端·毕业设计·课程设计·毕设
计算机-秋大田3 小时前
基于SpringBoot的环保网站的设计与实现(源码+SQL脚本+LW+部署讲解等)
java·vue.js·spring boot·后端·课程设计
汤姆yu3 小时前
基于springboot的高校物品捐赠系统
java·spring boot·后端·高校物品捐赠
magic 2453 小时前
深入理解Java网络编程:从基础到高级应用
java·开发语言
岁岁岁平安3 小时前
spring注解开发(Spring整合JUnit+MyBatis)(7)
java·spring·junit·log4j·mybatis
剑海风云4 小时前
JVM常用概念之垃圾回收设计与停顿
java·开发语言·jvm