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

目录

二、多线程代码

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)

相关推荐
S-X-S3 分钟前
Java面试题-Spring Boot
java·开发语言·spring boot
灵魂画师向阳7 分钟前
白嫖RTX 4090?Stable Diffusion:如何给线稿人物快速上色?
java·大数据·人工智能·ai作画·stable diffusion
ElseWhereR17 分钟前
C++中函数的调用
开发语言·c++
185的阿平18 分钟前
MVCC面试怎么答
java·mysql
Excuse_lighttime24 分钟前
堆排序
java·开发语言·数据结构·算法·排序算法
陈老师还在写代码26 分钟前
SpringBoot的单机模式是否需要消息队列?分布式应用中消息队列如何和服务的发现与注册、配置中心、SpringMVC相配合
java·spring boot·后端
arong_xu29 分钟前
理解C++ Type Traits
开发语言·c++·type_traits
等什么君!29 分钟前
spring 学习(spring-Dl补充(注入不同类型的数据))
java·学习·spring
新加坡内哥谈技术37 分钟前
ChunkKV:优化 KV 缓存压缩,让 LLM 长文本推理更高效
人工智能·科技·深度学习·语言模型·机器人
subject625Ruben43 分钟前
进阶版MATLAB 3D柱状图
开发语言·matlab·3d