【Java】多线程初阶

认识线程


概念

  1. 线程是什么
    1)线程就是一个执行流,每个线程按顺序执行自身代码;
    多个线程可以 "同时" 执行多份代码
    2)银行例子:
    公司办业务,张三(主线程)叫李四、王五一起处理转账、发福利、缴社保,3 个执行流共同完成同一个公司的任务,这就是多线程。张三是主线程 (Main Thread)

  2. 为什么要有线程
    1)并发编程是刚需
    a)单核 CPU 遇到性能瓶颈,多核 CPU 需要并发程序才能充分利用算力
    b)
    任务有 IO 等待场景:
    线程等待 IO 的时候,CPU 可以去执行别的任务,不浪费时间
    2)线程比进程更轻量
    a)创建线程更快
    b)
    销毁线程更快
    c) 操作系统调度线程更快
    **3)**进阶优化:线程池、协程

  3. 进程和线程的区别

    银行举例:多进程 = 多个客户各自办业务,票据互相隔离;
    多线程 = 同一个公司多个员工办业务,票据共享

  4. 4 种模型:
    1)单线程进程:1 个进程,里面只有 1 条执行流
    2)单进程多线程:1 个进程,内部多条执行流
    3) 多个单线程进程:多个独立进程,每个进程只有 1 个线程

    4) 多个多线程进程:多个独立进程,每个进程内部都有多条线程

  5. Java 线程 和 OS 操作系统线程的关系
    1)
    线程本质是操作系统内核 提供的机制,
    OS 提供 API(如 Linux pthread 库)给程序使用
    2) Java 里的Thread类,是对操作系统原生线程 API 的抽象和封装 。
    Java 线程底层就是操作系统线程

  6. 面试题
    1)线程是什么?
    线程是一条执行流,多个线程可以并发执行代码,
    同一个进程内线程共享内存资源
    2)进程和线程区别?
    ①进程是资源分配最小单位,线程是 CPU 调度最小单位;
    ②进程内存相互隔离,同一进程线程共享内存;
    ③进程包含线程,一个进程至少一个主线程;
    ④进程崩溃互不影响;线程崩溃可能造成整个进程退出;
    ⑤线程创建销毁调度开销远小于进程,更轻量。
    3)为什么用多线程?
    充分利用多核 CPU;IO 等待时让 CPU 执行其他任务;
    线程相比进程轻量,开销更小。
    4)Java Thread 和操作系统线程? Java 的 Thread 封装了操作系统原生线程,Java 线程本质就是操作系统线程。

第一个多线程程序

复制代码
   public class ThreadDemo {

       // 自定义线程类,继承Thread
       private static class MyThread extends Thread{
           @Override
           public void run() {

            Random random=new Random();
            while(true)
            {

                //打印当前线程名字
                System.out.println(Thread.currentThread().getName());

                try
                {
                    //休眠几秒
                    Thread.sleep(random.nextInt(10));
                }catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }

           }
       }

       public static void main(String[] args) {
           MyThread t1=new MyThread();

           t1.start(); //启动线程,自动调用run() 方法

           Random random=new Random();

           while(true)
           {
               // main 方法本身运行在主线程 ( main线程) 
               System.out.println(Thread.currentThread().getName());

               try
               {
                   Thread.sleep(random.nextInt(10));
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
       }
   }
  1. 线程概念
    1)每个线程都是独立执行流;多个线程并发 执行
    (操作系统快速切换,看起来同时运行)
    2)这里一共两个线程:
    a)main主线程:执行 main 函数里的 while 循环
    b)t1自定义子线程:执行 MyThread 里run()的 while 循环
    3)注意:调用 t1.start() 才是创建并启动线程;
    不能直接调用 t1.run (),直接调用 run 只是普通方法调用,不会创建新线程

  2. Thread.sleep()
    1)让当前线程休眠指定毫秒,进入阻塞状态
    2)会抛出受检异常 InterruptedException,必须 try-catch 捕获
    3)random.nextInt(10) 生成[0,9]随机整数,单位秒

  3. Thread.currentThread().getName()

    1)获取当前正在执行代码的线程名称。
    a)子线程默认名字:Thread-0
    b)主线程名字:main
    2)程序输出会交替打印 main 和 Thread-0,顺序随机,就是并发效果

  4. 易错点
    1)start():创建新线程,由 JVM 自动调用run() 开启多线程
    2)run():线程要执行的任务代码,直接调用只是普通方法,不会新建线程

创建线程的 5 种方式


方式 1:继承 Thread 类
复制代码
       //继承Thread 方式创建线程
      public static  class MyThread extends Thread
       {
           // 重写run 方法:线程执行任务
           @Override
           public void run() {
               System.out.println("这里是线程运行的代码");
           }
       }

       public static void main(String[] args) {

           // 创建自定义线程
           MyThread t=new MyThread();
           t.start();  //启动线程,自动调用run方法


       }

要点:

1)run():线程任务逻辑 ,写线程要执行的代码

2)start():启动线程 ,向操作系统申请创建线程,然后自动执行 run ();

不能直接调用 run () ,直接调用只是普通方法,不会创建新线程

3)在类内部 this 代表当前线程对象


方式 2:实现 Runnable 接口
复制代码
       // 实现Runnable 接口
      public static  class MyRunnable implements Runnable{

           //重写 run 方法 定义线程任务
           @Override
           public void run() {
               System.out.println("这里是线程运行的代码");
           }
       }

       public static void main(String[] args) {

           // 创建任务对象
           MyRunnable myRunnable =new MyRunnable();

           //将任务对象传入Thread 构造器 ,创建线程对象
           Thread t1=new Thread(myRunnable);
           
           t1.start(); // 调用start 启动线程

       }

要点:
1)Java 是单继承,如果类已经继承别的类,就只能用 Runnable 接口
2)this 代表 MyRunnable 对象,不是线程对象 ;
获取当前线程对象要用 Thread.currentThread()
2. 两种方式对比


简化写法(匿名内部类 + Lambda)
  1. 匿名内部类创建 Thread 子类

    复制代码
        public static void main(String[] args) {
    
            // 匿名内部类:继承Thread ,重写run方法
            Thread t1=new Thread()
            {
                @Override
                public void run() {
                    System.out.println("使用匿名内部类创建Thread 子类对象");
                }
            };
    
            // 启动线程
            t1.start();
    
        }

    1)new Thread(){ ... }

    a)这是匿名内部类,相当于快速创建一个继承Thread的子类,类没有名字。

    b)大括号{}里面重写父类 Thread 的run()方法,run()存放线程要执行的任务。

    2)t1.start()

    a)调用start(),JVM 会向操作系统申请创建新线程,线程就绪后自动执行run()。

    b) 如果写t1.run(),只是普通方法调用,不会开启新线程。

  2. 匿名内部类创建 Runnable

    复制代码
        public static void main(String[] args) {
            //匿名内部类实现Runnable接口,作为Thread的任务参数
    
            Thread t2=new Thread(new Runnable()
            {
                @Override
                public void run() {
                    System.out.println("使用匿名内部类创建 Runnable 子类对象");
                }
            });
    
            t2.start();
        }

    1)new Runnable(){ ... }:

    Runnable 是接口,这里创建匿名内部类实现 Runnable 接口,

    重写唯一抽象方法run(),run()里面就是线程要执行的任务。

    2)把这个匿名 Runnable 实例传入 Thread 构造方法,

    Thread 内部会持有这个任务对象。

    3)调用 t2.start()开启新线程,线程调度后自动执行 Runnable 里面的run()。

  3. Lambda 表达式
    1)Runnable 是函数式接口(只有一个抽象方法),可以用 lambda 简化

    复制代码
        public static void main(String[] args) {
    
            // 单行写法:lambda 表达式
            Thread t3=new Thread(()-> System.out.println("lambda创建线程"));
    
            // 多行写法:需要大括号包裹
            Thread t4=new Thread(()->
            {
                System.out.println("lambda多行写法");
                System.out.println("第二行任务代码");
            });
    
            // 启动线程
            t3.start();
            t4.start();
        }

    2)原理:Runnable 是函数式接口,只有一个抽象方法 run(),

    所以支持 Lambda 表达式简写。

    a)() -> 代码 就代表实现 Runnable 的 run 方法。

    b)():run 方法没有参数

    c)->:箭头符号,分隔参数和方法体

    3)两种 lambda 格式

    a)单行语句:可以省略大括号和 return

    复制代码
    () -> System.out.println("lambda创建线程")

    a)多条执行语句:必须加上 {}

    复制代码
    () -> {
        System.out.println("第一行");
        System.out.println("第二行");
    }

    4)本质等价:

    复制代码
    // lambda等价于前面的匿名Runnable
    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("匿名Runnable");
        }
    });
  4. 总结

    1)start() 和 run() 的区别

    a)run():只是普通方法,里面放任务代码,直接调用不会开启新线程

    b)start():native 方法,真正在 OS 创建线程,线程就绪后自动调用 run ()

    2)推荐优先使用 Runnable + Lambda :解耦、不占用继承、代码简洁

    3)线程启动只能调用start(),一个线程对象只能 start 一次,多次 start 抛异常


多线程的优势-增加运行速度


Thread 类


  1. Thread 是 JVM 用来管理线程的类:
    操作系统里每一个线程执行流,在 Java 中都会对应一个唯一的Thread对象
  2. Thread对象就相当于线程的 "档案卡片",里面记录线程信息:
    ID:线程 id
    1)线程名称(姓名)
    2)优先级
    3)线程状态(就绪 / 运行 / 阻塞等)
    4)其他属性
  3. JVM 会维护这些Thread对象,完成线程调度、线程管理 ,来决定 CPU 什么时候执行哪个线程

Thread 常见构造方法

这是 Java 多线程中Thread类的构造器,用来创建线程对象,
注意:new Thread () 只是创建线程对象,调用.start()才会真正启动线程

相关推荐
蜗牛互联网44 分钟前
Java 17 调用视觉 Responses API:JSON 约束与人工复核路由
java·人工智能·后端
SL_staff2 小时前
物模型三要素误用避坑指南:属性、事件、服务的正确建模逻辑
java
一条泥憨鱼2 小时前
【Redis | 黑马点评】短信登录功能 源码深度分析
java·redis·后端·session·黑马点评
威哥爱编程2 小时前
【AI全栈12-12】Spring Boot 3.x → 4.x 迁移实操:Jakarta 11、Jackson 3 与 AI 2.0
java·spring boot·后端
威哥爱编程2 小时前
【AI全栈12-11】Spring Boot 把 AI 接口真正上线扛量:限流 / 降级 / 可观测
java·spring boot·后端
小蒜学长2 小时前
基于SpringBoot+uni-app的闪洁家政系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·uni-app·家政服务
敲代码的彭于晏2 小时前
2026 K8s 零基础入门:手把手教你一步步搭建 Kubernetes 集群
java·运维·全栈
希洛兔2 小时前
状态模式详解:让对象随内部状态自动改变行为
java·设计模式·状态模式
需要8262 小时前
Spring Boot 启动流程:run() 里到底发生了什么
java·spring boot·后端