【多线程初阶篇 ²】创建线程的方式

目录

二、多线程代码

1.继承Thread类

2.实现Runnable接口

3.匿名内部类

[3.1 创建Thread⼦类对象](#3.1 创建Thread⼦类对象)

[3.2 创建Runnable⼦类对象](#3.2 创建Runnable⼦类对象)

4.lambda表达式(推荐)

小结:

🔥面试题:Java中创建线程都有哪些写法


二、多线程代码

1.继承Thread类

java 复制代码
package thread;

class MyThread extends Thread {
    @Override
    public void run() {
        //这里写的代码就是该线程要完成的工作
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new MyThread();
        //t.start();

        //没有创建出新的线程,就是在主线程中执行run中的循环打印(就是单线程了,串行执行)
        t.run();

        while (true) {
            System.out.println("hello main");
            t.sleep(1000);
        }
     }
}
  • 上述代码中其实有两个线程,一个t线程,一个main线程(主线程:JVM进程启动的时候,自己创建的线程)

  • 重写Thread方法中的run方法:描述线程需要完成的工作

    • 只是定义好,把这个方法的调用交给系统/其他的库/其他框架调用,(回调函数)
  • sleep方法:让当前线程主动进入"阻塞"状态,主动放弃在CPU上的执行,时间到了,才会解除,重新被调度到CPU上执行

  • start方法:调用操作系统提供创建线程的API,在内核中创建对应的PCB,并且把PCB加入到链表中,进一步的系统调度到这个线程之后,就会执行上述run方法的逻辑

  • Q1:如果开发中,发现你负责的服务器程序,消耗CPU资源超出预期,你如何排查?

    • 首先确认是哪个线程消耗的CPU比较高
    • 进一步排查,线程中是否有类似的"非常快速的"循环
    • 确认是否这里的循环一个这么快

      • 应该的话就可以升级更好的CPU
      • 如果不应该,说明需要在循环中引入一些等待操作
  • 上述代码结果顺序是不确定的:

    • 多线程的调度是无序的,即抢占式执行:任何一个线程 在执行任何一个代码的时候,都可能被其他线程抢占CPU资源

2.实现Runnable接口

java 复制代码
package thread;

class MyRunnable implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Demo2 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable());
        t.start();

        while (true) {
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
  • Runnable的作用:是描述一个"任务",表示"可执行的"

  • 和继承Thread类创建线程比较

    • 一是Thread自己记录要干啥
    • 二是Runnable记录要干啥,Thread负责执行
      好处:解耦合,把任务和线程拆分开,把这样的任务给其他地方执行

3.匿名内部类

3.1 创建Thread⼦类对象
java 复制代码
package thread;

public class Demo3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread() {
            @Override
            public void run() {
                while(true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };

        t.start();

        while (true) {
            System.out.println("hello main");
            Thread.sleep(1000);
        }

    }
}
  • 过程

    • 创建一个Thread子类
    • 同时创建一个该子类的实例
      但是对于匿名内部类来说,只能创建这一个实例,因为拿不到名字
    • 子类内部重新父类的run方法
  • 好处:简单


3.2 创建Runnable⼦类对象
java 复制代码
package thread;

public class Demo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println("hello thread");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });

        t.start();

        while (true) {
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}
  • 过程:和3.1代码一样

  • 区别:此处的匿名内部类只是针对Runnable,和Thread没有关系
    只是把Runnable的实例,作为参数传入Thread的构造方法中

  • Q1:为什么在main方法中处理sleep异常有两种选择,而线程run中的sleep方法只有一种

    • main方法可以

      • throws
      • try catch
    • 线程中的run方法
      • 只能try catch
    • 因为父类run中没有抛出异常,由于重写要求方法签名是一样的的,也无法抛出异常
      throws其实是方法签名的一部分(方法名字+方法的参数列表【不包含返回值和pubic/private】+声明抛出的异常)

4.lambda表达式(推荐)

java 复制代码
package thread;

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t.start();

        while (true) {
            System.out.println("hello main");
            Thread.sleep(1000);
        }
    }
}
  • 本质就是一个"匿名函数",一次性函数,用完就丢

小结:

  • 5种方法都是要把线程的任务内容表示出来
  • 通过Thread的start来创建/启动系统中的线程
    Thread对象和操作系统中的线程是一一对应的关系

🔥面试题:Java中创建线程都有哪些写法

  • 继承Thread类

  • 实现Runnable接口

  • 匿名内部类

    • 创建Thread子类
    • 创建Runnable子类
  • lambda表达式

  • 实现Callable接口(TODO)

  • 使用线程池(TODO)

相关推荐
杨杨杨大侠8 小时前
都能接模型、RAG 和 MCP,Java Agent 框架到底差在哪?
java
破茧蝴蝶8 小时前
Python分布式爬虫完整教程
开发语言·python
燧机科技SuiJi8 小时前
山体坍塌落石滑坡泥石流监测系统 落石泥石流监控摄像机
大数据·人工智能·深度学习·yolo
狗凯之家源码网8 小时前
全新 PHP 轻量客服聊天室源码深度评测
开发语言·php·聊天室系统
远离UE49 小时前
UE5 Substrate Thin Film 源码拆析
开发语言·javascript·ue5
keyipatience9 小时前
JavaScript学习(1)书写位置,变量、数据类型、运算符
开发语言·前端·javascript·css·学习
Thinker QAQ9 小时前
并发编程(九):BlockingQueue 与 Channel——语言层的原子性、可见性与有序性
java·python·go·并发编程·channel·blockingqueue·message passing
栈帧之外10 小时前
【C++】STL之set
开发语言·c++
霸道流氓气质10 小时前
RAG 可观测性与生产监控实战:四层遥测模型、五大仪表盘与 OpenTelemetry 全链路追踪的 Java 生产级实现
java·人工智能·python
AOI小白新手上路10 小时前
autodl常用的tar 打包下载(数据集)命令手册:逐段拆解与套用模板
java·大数据·数据库