Java多线程——2.创建新线程
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("主线程执行完毕");
}
}
执行流程解析:
- 主线程创建
MyThread实例 - 调用
start()方法启动新线程,JVM会创建一个新的执行线程 - 新线程执行
run()方法中的代码 - 主线程和新线程并发执行
方式二:实现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()方法的工作原理:
- 向JVM请求创建一个新线程
- JVM分配线程所需的资源
- 新线程开始执行
run()方法中的代码 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,当线程在休眠期间被中断时会触发该异常。
线程的生命周期
一个线程从创建到结束,会经历以下状态:
- 新建状态(New):线程对象创建但未调用
start()方法 - 就绪状态(Runnable):调用
start()方法后,等待CPU调度 - 运行状态(Running):获得CPU时间片,执行
run()方法 - 阻塞状态(Blocked):因某种原因暂停执行(如等待锁、I/O操作)
- 死亡状态(Terminated):
run()方法执行完毕或因异常终止
理解线程的生命周期有助于更好地调试多线程程序,避免出现线程泄漏或死锁等问题。
总结
- 创建线程有两种方式:继承
Thread类或实现Runnable接口 - 必须调用
start()方法才能启动新线程,直接调用run()方法无效 - 线程调度由操作系统控制,优先级可以影响但不能决定执行顺序
Thread.sleep()方法可以暂停线程执行,让出CPU时间片- 一个线程对象只能启动一次,多次启动会抛出异常
- 理解线程的生命周期
Java多线程——2.创建新线程的更多相关文章
- 廖雪峰Java11多线程编程-1线程的概念-2创建新线程
Java语言内置多线程支持: 一个Java程序实际上是一个JVM进程 JVM用一个主线程来执行main()方法 在main()方法中又可以启动多个线程 1.创建新线程 1.1 方法一:使用Thread ...
- Java多线程——<三>简单的线程执行:Executor
一.概述 按照<Java多线程——<一><二>>中所讲,我们要使用线程,目前都是显示的声明Thread,并调用其start()方法.多线程并行,明显我们需要声明多个 ...
- Java多线程(一) —— 线程的状态详解
一.多线程概述 1. 进程 是一个正在执行的程序.是程序在计算机上的一次运行活动. 每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控制单元. 系统以进程为基本单位进行系统资源的调度 ...
- java多线程(四)-自定义线程池
当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的 ...
- java多线程系列六、线程池
一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池. 2. 使用线程池的好处 a) 降低资源的消耗.使用线程池不用频繁的创建线程和销毁线程 b) 提高响应速度,任 ...
- Java多线程(1) 创建
一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下以下这张较为经典的图: Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Threa ...
- Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)
一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会 ...
- Java多线程(五)线程的生命周期
点我跳过黑哥的卑鄙广告行为,进入正文. Java多线程系列更新中~ 正式篇: Java多线程(一) 什么是线程 Java多线程(二)关于多线程的CPU密集型和IO密集型这件事 Java多线程(三)如何 ...
- Java多线程-同步:synchronized 和线程通信:生产者消费者模式
大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...
- Java多线程的创建方法
Java 线程类也是一个 object 类,它的实例都继承自java.lang.Thread 或其子类. 可以用如下方式用 java 中创建一个线程,执行该线程可以调用该线程的 start()方法: ...
随机推荐
- Qt项目转Visual Studio项目
有时觉得在Qt中调试代码不够方便,无法调试更深层次的参数,故需要将Qt项目转为Visual Studio项目. 方法:qmake -tp vc Hello.pro 以上命令会将名为Hello的Qt工程 ...
- java list<对象>根据某个字段分组
前言 仅供学习参考,不保证性能问题 其中的实体类改成你自己的实体类 代码 /** * 根据某个字段进行分组,分组后遍历方法 * <p> * Map<String, List<M ...
- 【2020.11.24提高组模拟】变换 (transform) 题解
[2020.11.24提高组模拟]变换 (transform) 题解 题意描述 给一个大小为\(n\)的\(01\)环\(A\),点编号为\(0,1,\dots,n-1\).每一个点\(i\)都与\( ...
- windows下大数据开发环境搭建(5)——Hive环境搭建
本文介绍如何在windows下搭建Hive开发环境,主要依赖的环境是Java和Hadoop,其他大部分工作主要是动手配置的工作,按照下面的介绍一步步操作即可完成搭建. 一.所需环境 1.Java:wi ...
- ceph集群故障运维--持续更新
一.PG处于异常状态active+undersized+degraded 部署环境: 自己搭建的3节点集群,集群共5个OSD,部署Ceph的RadosGW的服务时,副本默认设置为3,集群存放数据量少. ...
- Coze工作流实战:一键生成像素风格视频
前言 最近像素画风的视频非常火,一个视频浏览量超过10w+的也有很多. 那么这个是怎么实现的? 其实,通过AI工作流可以比较简单地实现这样的短视频. 今天给大家分享一下,我是如何搭建工作流实现的. 欢 ...
- Web前端入门第 68 问:JavaScript 事件循环机制中的微任务与宏任务
JS 是单线程语言.这句话对不对? 按照目前的情况来看,JS 自从支持了 Web Worker 之后,就不再是单线程语言了,但 Worker 的工作线程与主线程有区别,在 Worker 的工作线程中无 ...
- Cursor再见!又一AI编程神器!简单两步,Augment无限续杯,爽用Claude 4!
1.Augment Code 介绍 Augment Code 是一款 AI 驱动的编程工具,基于 Anthropic 的 Claude Sonnet 4 模型构建,支持高达 20 万 token 的上 ...
- Android 各个模拟器的 链接上Adb 设备管理的方法
https://blog.csdn.net/weixin_45598506/article/details/107918803?spm=1001.2101.3001.6661.1&utm_me ...
- C# lambda 去除重复GroupBy 查找最大记录一条数据
public class Student { public int ID { get; set; } public string Name { get; set; } public int Age { ...