Java多线程

线程的基本概念

当一个Java程序启动时,实际上是启动了一个JVM进程,JVM会创建一个主线程来执行main()方法。除此之外,我们还可以创建多个额外的线程,这些线程可以并发执行不同的任务,从而充分利用多核CPU的计算能力。

  • 进程:一个正在运行的程序,拥有独立的内存空间和系统资源
  • 线程:进程内部的执行单元,共享进程的资源,拥有独立的执行路径
  • 多线程:一个进程中同时运行多个线程,实现并发执行

创建新线程的两种方式

Java提供了两种主要方式来创建新线程,各有其适用场景和特点。

方式一:继承Thread类并覆写run()方法

Thread类是Java中线程操作的核心类,通过继承该类并覆写其run()方法,可以定义线程要执行的任务:

// 自定义线程类
class MyThread extends Thread {
@Override
public void run() {
// 线程要执行的任务代码
System.out.println("新线程执行中...");
for (int i = 0; i < 5; i++) {
System.out.println("子线程: " + i);
try {
// 让线程暂停500毫秒
Thread.sleep(500);
} catch (InterruptedException e) {
// 处理中断异常
e.printStackTrace();
}
}
System.out.println("新线程执行完毕");
}
} // 测试类
public class ThreadExample {
public static void main(String[] args) {
System.out.println("主线程启动"); // 创建线程实例
MyThread thread = new MyThread();
// 启动线程
thread.start(); System.out.println("主线程继续执行");
for (int i = 0; i < 3; i++) {
System.out.println("主线程: " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主线程执行完毕");
}
}

执行流程解析

  1. 主线程创建MyThread实例
  2. 调用start()方法启动新线程,JVM会创建一个新的执行线程
  3. 新线程执行run()方法中的代码
  4. 主线程和新线程并发执行

方式二:实现Runnable接口并覆写run()方法

Runnable接口定义了一个单一的run()方法,通过实现该接口并将实例传递给Thread类,可以更灵活地创建线程:

// 实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
// 线程要执行的任务代码
System.out.println("新线程执行中...");
// 任务逻辑...
}
} // 测试类
public class RunnableExample {
public static void main(String[] args) {
System.out.println("主线程启动"); // 创建Runnable实例
MyRunnable runnable = new MyRunnable();
// 将Runnable实例传递给Thread
Thread thread = new Thread(runnable);
// 启动线程
thread.start(); System.out.println("主线程继续执行");
// 主线程逻辑...
}
}

使用Lambda表达式简化(Java 8+):

public class LambdaThreadExample {
public static void main(String[] args) {
System.out.println("主线程启动"); // 使用Lambda表达式创建线程
Thread thread = new Thread(() -> {
System.out.println("新线程执行中...");
// 任务逻辑...
});
thread.start(); System.out.println("主线程继续执行");
// 主线程逻辑...
}
}

两种方式的对比

方式 优点 缺点 适用场景
继承Thread 代码简单,直接使用this指代当前线程 无法继承其他类(Java单继承) 简单的线程任务
实现Runnable 可继承其他类,适合多个线程共享资源 代码稍复杂,需通过Thread.currentThread()获取当前线程 复杂任务,资源共享场景

推荐实践:优先使用实现Runnable接口的方式,因为它更灵活,也符合面向接口编程的思想。

线程的启动与执行

创建线程实例后,必须调用start()方法才能真正启动一个新线程。需要特别注意的是,直接调用run()方法并不会创建新线程,而只是在当前线程中执行run()方法中的代码:

public class ThreadStartExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("线程执行中,当前线程名: " + Thread.currentThread().getName());
}); // 正确启动新线程
thread.start(); // 输出: 线程执行中,当前线程名: Thread-0 // 错误方式,不会创建新线程
thread.run(); // 输出: 线程执行中,当前线程名: main
}
}

start()方法的工作原理:

  1. 向JVM请求创建一个新线程
  2. JVM分配线程所需的资源
  3. 新线程开始执行run()方法中的代码
  4. start()方法立即返回,不等待run()方法执行完毕

一个线程对象只能调用一次start()方法,多次调用会抛出IllegalThreadStateException异常。

线程调度与优先级

线程的调度由操作系统和JVM共同负责,程序员无法精确控制线程的执行顺序,但可以通过设置线程优先级来影响调度器的决策。

线程优先级

Java中线程优先级分为1到10共10个级别,默认优先级为5:

public class ThreadPriorityExample {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println("线程1: " + i);
}
}); Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println("线程2: " + i);
}
}); // 设置优先级
t1.setPriority(Thread.MIN_PRIORITY); // 优先级1
t2.setPriority(Thread.MAX_PRIORITY); // 优先级10 t1.start();
t2.start();
}
}

注意事项

  • 优先级高的线程获得的CPU时间片通常更多,但不保证一定先执行
  • 不同操作系统对优先级的支持不同,不能过度依赖优先级来控制执行顺序
  • 优先级设置应合理,避免设置过多不同优先级的线程

线程休眠

使用Thread.sleep(long millis)方法可以让当前线程暂停执行指定的毫秒数:

public class ThreadSleepExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
System.out.println("线程开始执行");
Thread.sleep(2000); // 休眠2秒
System.out.println("线程休眠结束");
} catch (InterruptedException e) {
System.out.println("线程被中断");
return;
}
System.out.println("线程执行完毕");
}); thread.start();
}
}

sleep()方法会抛出InterruptedException,当线程在休眠期间被中断时会触发该异常。

线程的生命周期

一个线程从创建到结束,会经历以下状态:

  1. 新建状态(New):线程对象创建但未调用start()方法
  2. 就绪状态(Runnable):调用start()方法后,等待CPU调度
  3. 运行状态(Running):获得CPU时间片,执行run()方法
  4. 阻塞状态(Blocked):因某种原因暂停执行(如等待锁、I/O操作)
  5. 死亡状态(Terminated)run()方法执行完毕或因异常终止

理解线程的生命周期有助于更好地调试多线程程序,避免出现线程泄漏或死锁等问题。

总结

  • 创建线程有两种方式:继承Thread类或实现Runnable接口
  • 必须调用start()方法才能启动新线程,直接调用run()方法无效
  • 线程调度由操作系统控制,优先级可以影响但不能决定执行顺序
  • Thread.sleep()方法可以暂停线程执行,让出CPU时间片
  • 一个线程对象只能启动一次,多次启动会抛出异常
  • 理解线程的生命周期

Java多线程——2.创建新线程的更多相关文章

  1. 廖雪峰Java11多线程编程-1线程的概念-2创建新线程

    Java语言内置多线程支持: 一个Java程序实际上是一个JVM进程 JVM用一个主线程来执行main()方法 在main()方法中又可以启动多个线程 1.创建新线程 1.1 方法一:使用Thread ...

  2. Java多线程——<三>简单的线程执行:Executor

    一.概述 按照<Java多线程——<一><二>>中所讲,我们要使用线程,目前都是显示的声明Thread,并调用其start()方法.多线程并行,明显我们需要声明多个 ...

  3. Java多线程(一) —— 线程的状态详解

    一.多线程概述  1. 进程 是一个正在执行的程序.是程序在计算机上的一次运行活动. 每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控制单元. 系统以进程为基本单位进行系统资源的调度 ...

  4. java多线程(四)-自定义线程池

    当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...

  5. java多线程系列六、线程池

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...

  6. Java多线程(1) 创建

    一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下以下这张较为经典的图: Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Threa ...

  7. Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

    一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...

  8. Java多线程(五)线程的生命周期

    点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...

  9. Java多线程-同步:synchronized 和线程通信:生产者消费者模式

    大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...

  10. Java多线程的创建方法

    Java 线程类也是一个 object 类,它的实例都继承自java.lang.Thread 或其子类. 可以用如下方式用 java 中创建一个线程,执行该线程可以调用该线程的 start()方法: ...

随机推荐

  1. Qt项目转Visual Studio项目

    有时觉得在Qt中调试代码不够方便,无法调试更深层次的参数,故需要将Qt项目转为Visual Studio项目. 方法:qmake -tp vc Hello.pro 以上命令会将名为Hello的Qt工程 ...

  2. java list<对象>根据某个字段分组

    前言 仅供学习参考,不保证性能问题 其中的实体类改成你自己的实体类 代码 /** * 根据某个字段进行分组,分组后遍历方法 * <p> * Map<String, List<M ...

  3. 【2020.11.24提高组模拟】变换 (transform) 题解

    [2020.11.24提高组模拟]变换 (transform) 题解 题意描述 给一个大小为\(n\)的\(01\)环\(A\),点编号为\(0,1,\dots,n-1\).每一个点\(i\)都与\( ...

  4. windows下大数据开发环境搭建(5)——Hive环境搭建

    本文介绍如何在windows下搭建Hive开发环境,主要依赖的环境是Java和Hadoop,其他大部分工作主要是动手配置的工作,按照下面的介绍一步步操作即可完成搭建. 一.所需环境 1.Java:wi ...

  5. ceph集群故障运维--持续更新

    一.PG处于异常状态active+undersized+degraded 部署环境: 自己搭建的3节点集群,集群共5个OSD,部署Ceph的RadosGW的服务时,副本默认设置为3,集群存放数据量少. ...

  6. Coze工作流实战:一键生成像素风格视频

    前言 最近像素画风的视频非常火,一个视频浏览量超过10w+的也有很多. 那么这个是怎么实现的? 其实,通过AI工作流可以比较简单地实现这样的短视频. 今天给大家分享一下,我是如何搭建工作流实现的. 欢 ...

  7. Web前端入门第 68 问:JavaScript 事件循环机制中的微任务与宏任务

    JS 是单线程语言.这句话对不对? 按照目前的情况来看,JS 自从支持了 Web Worker 之后,就不再是单线程语言了,但 Worker 的工作线程与主线程有区别,在 Worker 的工作线程中无 ...

  8. Cursor再见!又一AI编程神器!简单两步,Augment无限续杯,爽用Claude 4!

    1.Augment Code 介绍 Augment Code 是一款 AI 驱动的编程工具,基于 Anthropic 的 Claude Sonnet 4 模型构建,支持高达 20 万 token 的上 ...

  9. Android 各个模拟器的 链接上Adb 设备管理的方法

    https://blog.csdn.net/weixin_45598506/article/details/107918803?spm=1001.2101.3001.6661.1&utm_me ...

  10. C# lambda 去除重复GroupBy 查找最大记录一条数据

    public class Student { public int ID { get; set; } public string Name { get; set; } public int Age { ...