多线程的创建与管理

🚗🚗🚗🚗🚗🚗🚗 数据结构专栏🚗🚗🚗🚗🚗🚗🚗🚗🚗🚗

🛹🛹🛹🛹🛹🛹🛹小知识总结分享🛹🛹🛹🛹🛹🛹🛹🛹🛹🛹

🚀🚀🚀🚀🚀🚀🚀题目历练场🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀

🚢🚢🚢🚢🚢🚢🚢功能包与集合类介绍🚢🚢🚢🚢🚢🚢🚢🚢🚢

🛞🛞🛞🛞🛞🛞🛞java EE🛞🛞🛞🛞🛞🛞🛞🛞🛞🛞🛞🛞🛞

文章目录


大家好耶,今天书接上回,了解了计算机是怎么工作的后,这篇文章就来说说啥是线程,thread的常见方法,手动创建一个线程~ 打开线程是怎么在java中创建的.话不多说~ 让我们开始吧~

一、 线程的理解

可以理解线程就是一个"执行流 ",每个线程中都是一系列的代码逻辑,每个线程与线程都会按照顺序执行自己的代码.

  • 为什么要引入这个线程呢~

解决并发问题

  • 之前大都是单核的,可是随着时代进步.多线程是不可避免的.举一个例子,之前手机只能单个运行一个APP,现在可能多个执行,比如手机的小窗模式~

线程编程的优势

  • 提高程序并发性:线程允许程序同时执行多个任务,例如在图形界面程序中,一个线程处理用户输入,另一个线程执行后台计算。
  • 资源开销低:线程共享进程资源(如内存、文件句柄),创建和切换的开销远小于进程。
  • 简化复杂任务:多线程可将任务拆分为并行执行的子任务,如Web服务器同时处理多个客户端请求。
  • 利用多核性能:多线程程序能有效利用多核CPU的并行计算能力,加速任务完成。

线程与进程的联系

还有一个名词就是进程,其实线程是进程的子集.

  • 每个进程至少存在一个线程,就是主线程~
  • 进程与进程之间不共享内存空间~,但是在同一个进程的多个线程之间会资源共享,下买是图就会好理解.
  • 注意,虽然感觉进程比较大,但是进程是系统分配资源的最小单位.
  • 线程是系统调度的最小单位.
  • 一个进程挂了一般不会影响其他进程,但是一个进程中的线程挂了,可能会影响这个进程中的其他线程.

二、 Thread类

thread类是java中用来创建与控制线程的

  • java.lang.Thread
  • 用来 创建和控制线程,实现多线程
  • 有两种常用写法:
    1. 继承 Thread 类
    2. 实现 Runnable 接口

常用构造方法

java 复制代码
Thread()                     
Thread(String name)          
Thread(Runnable target)      
Thread(Runnable target, String name)

常用方法

  • id是线程的唯一标识,不同线程不会重复

  • 状态表示线程当前所处的情况

  • 前台线程 默认创建的线程都是前台线程~

  • 后台线程:前台线程全死 → 它跟着死

    • 怎么使用呢~,就是通过我们类继承或者实现接口,这是总的大方向,下面呢我会距离5种常见的线程实现方式

注.start .join等方法,会在下面的实例中在做解释~

三、 第一个多线程程序

下面呢,咱们就通过java搞一个多线程的程序.进一步认识线程是如何创建的,理解其中的方法.

方法1,继承thread类

java 复制代码
    //第一个多线程程序
    //1创建子类,继承thread 重写run
    class MyThread extends Thread{
        public void run(){
            while ( true){
                System.out.println("线程");
            try {
                Thread.sleep(1000);
                //.sleep是thread类中的方法,是线程休眠的,休眠时间是毫秒
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
          }
        }
    }
public class demo3 {
    public static void main(String[] args) throws InterruptedException {
        //调用自己的线程类
       Thread t = new MyThread();
       t.start();  //.start()启动线程

       while (true){
           System.out.println("线程启动了");
           Thread.sleep(1000);
       }
    }
}

我们就来说说这个代码的重要地方

首先创建一个子类需要继承Thread类,重写其中的run方法,为啥子要在run中书写代码逻辑呢? 实际上.

  • run () 是线程真正执行的入口,线程启动后只会自动调用 run (),不会调用你写的其他方法。.start后就会寻找run(),进一步启动线程.
  • sleep是Thread类中的静态方法,通过.sleep.让当前线程休眠一会,不会释放锁(后面文章会详细说~*会抛出interruptdedexception异常
  • main方法中就是主线程,注意哈,主线程可以理解为我们普通的代码逻辑.是不要start类似的进行调用开始的.main就是开始.

所以嘞上面就是一样的代码逻辑,只不过次线程要想实现就要进程thread类,并且.start启动线程~

执行结果:

  • 通过这个执行结果我们也不难方法,这里的调度是随机的,并不是有规律的.

方法2,实现Runnable接口

runnable接口

Runnable 是 Java 提供的一个函数式接口,只有一个抽象方法:run ().作用:专门用来定义线程要执行的任务。
那为什么我们都可以用继承thread类实现多线程了,还要搞一个借口呢

  • 最最主要的原因就是java是单继承的 ,如果你的类已经继承了别的类,就不能再 extends Thread .不光是线程的创建,好多其他模式也是多种创建模式~

  • 实际上继承的thread类也是实现了runnable接口的,最重要的run方法还是在这个接口中~

Runnable 和 Thread 的关系

Runnable = 任务(要做什么)
Thread = 线程(谁去做)

  • 写 Runnable 定义任务,也就是run中代码逻辑~
  • 交给 Thread
  • 调用 start() 才会真正开线程
java 复制代码
class MyRunnable implements Runnable{
    public void run(){
        while (true){
            System.out.println("线程");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
public class demo4 {
    public static void main(String[] args) {
        //实例化任务
       // Runnable task = new MyRunnable();  这种只能调用runnable接口的方法
        MyRunnable r = new MyRunnable();
        Thread t = new Thread(r);  //把具体的任务对象作为参数传递给Thread类
        t.start();

        //主线程
        while (true){
            System.out.println("线程启动了");
            try {
                Thread.sleep(1000);   //有sleep:主动让出CPU时间片,让其他线程有机会执行
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

}

我们说一下这个代码细节遇上面的区别

  • 上面是在thread中直接run代码逻辑.就是线程中就是有任务栏
  • 咱们这个则是先是创建一个任务,然后呢实例化任务的对象,在放在thread类中(存在相关的构造方法,上面有提到啦~)
  • 但是下面这个是更加解耦合的,更加推荐~

方法三、匿名内部类创建Thread子类对象

这个本质上与第一个是相同的~.啥是匿名内部类呢?

匿名内部类

  • 匿名内部类 = 没有名字、只用一次的类
  • 作用 = 少写代码,快速创建接口 / 父类的对象
  • 在多线程里 = 快速创建 Runnable 任务,不用单独写类
java 复制代码
public class demo5 {
    //匿名内部类创建Thread子类对象
    public static void main(String[] args) {
        Thread t = new Thread(){
            public void run(){
                while (true){
                    System.out.println("线程");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        t.start();

//主线程
        while (true){
            System.out.println("线程启动了");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
}

方法四、匿名内部类创建Runnable子类对象

java 复制代码
public class demo6 {
    //实现runnable 实现内部类
    public static void main(String[] args) {
     Thread t = new Thread(new Runnable() {
         @Override
         public void run() {
             while (true){
                 System.out.println("线程");
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     throw new RuntimeException(e);
                 }
             }
         }
     });
    t.start();
    //主线程
    while (true){
        System.out.println("线程启动了");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    
    }
}

方法五、lambda表达式创建Runnable子类对象(最推荐的),本质上与4相同.

java 复制代码
public class demo7 {
    //lambda表达式创建Runnable子
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true){
                System.out.println("线程启动了");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
      t.start();
    }
}

线程的管理

  • 这是大概的过程就是存在一些等待 ,加锁等其他的thread方法让进程进一步一步执行.下面我们就按照创建线程 -> 启动线程 ->中断线程 ,等待线程, 休眠线程这几个状态展开看看.

启动线程.strat

  • 启动线程 : 本质上是会调用操作系统的api,在内部创建一个线程出来.一个进程是通过多个pcd进行调度进行的.在java中通过start在操作系统中会创建一个pcb并纳入链表,等待调度,就像进程的范围扩大了

中断线程

  • java 中并不提供强制终止,所有的让线程终止的犯法,都是围绕着"让入口方法结束",也就是run方法
    有几种常见的控制方法

标志位控制

java 复制代码
import java.util.Scanner;

public class demo8 {
    private static boolean running = true;
    public static void main(String[] args) {
        Thread t = new Thread(() -> {
           while (running){
               System.out.println("线程启动了");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
           System.out.println("t线程结束");
        });
        t.start();

        //主线程
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入1退出线程");
        int n = sc.nextInt();
        if (n == 1){
            running = false;
        }

    }
}

  • 注意 这里lambda中running是类变量,直接引用来的,会因为外界改变而改变.
    但是在lambda内部类中的局部变量捕获是必须是final或者是事实final 没有被修改的.本质上拷贝变量,当外部变量发生变化后,背拷贝的不在随之更改.

Lambda / 匿名内部类中使用的是:

  • 类变量(静态变量)、成员变量
    直接引用原来的变量
    → 外部修改,内部会跟着变

如果使用的是:

  • 局部变量(方法里的变量)
    → 本质是拷贝一份值
    → 必须是 final 或 事实final(没被修改过)
    → 外部后来再变,内部拷贝的不会跟着改

中断机制

java 复制代码
import java.util.Scanner;

public class demo9 {

    public class demo8 {
        public static void main(String[] args) {
            Thread t = new Thread(() -> {
                Thread cur = Thread.currentThread(); 
                //获取当前线程isInterrupted()判断当前线程是否被中断 默认是没有中断的
                //默认是false  取反就是 true
                while (!cur.isInterrupted()) {
                    System.out.println("线程启动了");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.println("t线程结束");
            });
            t.start();
            //主线程
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入1退出线程");
            int n = sc.nextInt();
            if (n == 1) {
                t.interrupt(); //使得线程中断
            }
        }
    }
}
java 复制代码
import java.util.Scanner;

public class demo10 {
        public static void main(String[] args) {
            Thread t = new Thread(() -> {
                while (true) {
                    System.out.println("线程启动了");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                      break;
                    }
                }
                System.out.println("t线程结束");
            });
            t.start();
            //主线程
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入1退出线程");
            int n = sc.nextInt();
            if (n == 1) {
                t.interrupt(); //使得线程中断
            }
        }
    }
  • 注意: interrupt方法会设置标志位false ->true,并且唤醒sleep等阻塞方法,但是一旦sleep唤醒,通过异常的方式返回,回把前线程标志位重新设置为false.设置break即可

等待线程

举一个生活中的例子~

主线程 = 你,t 线程 = 外卖员

t.join() = 你等着外卖员送到,再吃饭

不 join:你该干嘛干嘛,外卖员送他的

join:你卡住不动,一直等外卖员送到

  • 需要等待⼀个线程完成它的⼯作后,才能进⾏⾃⼰的下⼀步⼯作 如果调用join之前,t就已经执行完了,此时join就不在堵塞了.

等待线程T

java 复制代码
public class demo10 {
    //主线程等待
    private static int result = 0;
    public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        // 子线程计算
        for (int i = 0; i < 100; i++) {
            result += i;
        }
    });

    t1.start();
    t1.join();  // 主线程等待t1执行完毕

        System.out.println( result);
        int result2 = 0;
        //主线程
        for (int i = 0; i < 5000; i++) {
            result2 += i;
        }
    System.out.println(result2);  // 确保看到最终结果
}
}

等待主线程

java 复制代码
public class demo10 {


        //主线程等待
        private static long result = 0;

        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(() -> {
                // 子线程计算
                for (int i = 0; i < 100; i++) {
                    result += i;
                }
            });

            t1.start();
            t1.join();  // 主线程等待子线程执行完毕

            System.out.println(result);  // 确保打印最终结果
        }
    }

线程休眠

线程休眠:让当前正在运行的线程暂时停下来,不占用 CPU,等一段时间后再自动恢复运行。

总结

这篇文章就先分享到这里了,希望对你有帮助~

我是Dylan,下次见~

相关推荐
今心上1 小时前
关于json的理解测试!!
开发语言·json
枫叶丹42 小时前
【Qt开发】Qt界面优化(六)-> Qt样式表(QSS) 伪类选择器
c语言·开发语言·c++·qt
玄〤2 小时前
个人博客网站搭建day3--Spring Boot JWT Token 认证配置的完整实现详解(漫画解析)
java·spring boot·后端·jwt
NaCl鱼呜啦啦2 小时前
static 实例 vs 单例模式
开发语言·单例模式
0 0 02 小时前
CCF-CSP 34-2 矩阵重塑(其二)(reshape2)【C++】考点:矩阵转置模拟
开发语言·c++·算法·矩阵
一叶之秋14122 小时前
窗口基石:掌控 Qt 界面的无限形态
开发语言·qt
马猴烧酒.2 小时前
【JAVA算法|hot100】堆类型题目详解笔记
java·开发语言·笔记
Drifter_yh2 小时前
「JVM」Java 垃圾回收机制全解析:回收算法、分代流转与 G1 收集器底层拆解
java·jvm·算法
天一生水water2 小时前
LangChain的智能体教程
开发语言·人工智能·langchain·php·智慧油田