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. 基于Streamlit的BS直聘数据爬取可视化平台(爬虫)

    一.项目介绍 本项目是一个基于Streamlit和Selenium的BOSS直聘职位数据爬虫系统,提供了友好的Web界面,支持自定义搜索条件.扫码登录.数据爬取和导出等功能. 1.1 功能特点 支持多 ...

  2. AI智能体的技术架构与解决方案

    相比于传统软件,AI智能体是一个新兴事物,技术架构和解决方案仍处在高速迭代中.所以,本文章将重点放在理清AI智能体相关的技术脉络,而非具体技术实现.更多相关知识还需要读者通过第三方搜索等方式,保持与时 ...

  3. 运用servlet来实现文件的上传与下载

    文件的上传: 1 /** 2 * 1. 文件的上传必须使用post请求,因为get请求的数据是在 url地址上, 但是url地址 3 * 能够携带数据大小是有限:2k 4k 4 * 2. 文件上传必须 ...

  4. 你了解CAS吗?有什么问题吗?如何解决?

    什么是CAS? CAS全称Compare And Swap,比较与交换,是乐观锁的主要实现方式.CAS在不使用锁的情况下实现多线程之间的变量同步.ReentrantLock内部的AQS和原子类内部都使 ...

  5. HyperWorks分析模型的建立与边界条件施加

    在HyperWorks完成了基本的网格剖分后,还需要赋予模型各类与求解相关的信息,方能最终生成可以递交求解器计算的输入文件.分析模型的建立与载荷及边界条件的创建包括以下内容: -定义待输出的目标求解器 ...

  6. 基于 StarRocks 的指标平台查询加速方案

    项目背景 指标管理平台按指标查询类型可以划为落表指标和即席查询指标. 落表指标:可选择不同的维度生成多个结果表(每天提交任务写入结果表),对指标进行取数的时候会根据查询条件自动匹配最合适的结果表进行查 ...

  7. ET框架服务端部署到Linux

    运行环境:Centos7.3 + JetBrains Rider 2020 + ET 5.0 + Mac笔记本 (这里要说一下低于Centos7的没法安装.net core环境) 服务器和资源服务部署 ...

  8. IDEA热布署报错java.lang.IllegalStateException: Restarter has not been initialized

    *************************** APPLICATION FAILED TO START *************************** Description: Bin ...

  9. Blazor学习之旅 (13) Razor类库的使用

    在上一篇我们学习了Blazor和JavaScript的互操作性,这一篇我们了解下如何创建和使用Razor类库. 什么是Razor类库? 我们都知道,在.NET应用程序中,我们可以通过NuGet来安装各 ...

  10. 如何基于three.js(webgl)引擎架构,实现3D医院、3D园区导航,3D科室路径导航

    一.前言 在这个日新月异的科技时代,技术的价值不仅在于推动社会的进步,更在于如何更好地服务于人民.医院三维导航系统项目,正是技术服务于人民理念的生动体现,真正从患者出发,解决患者在复杂的医院园区找科室 ...