当前位置: 首页 > news >正文

java学习笔记——多线程

一、多线程基础知识

1.概念

(1)程序:静态的代码,存储在硬盘上的 .class 文件。

进程:正在运行的程序,是系统分配资源的最小单位。

线程:进程中的执行单元,是CPU调度的最小单位。

(2)多线程:一个进程中有多个线程并发执行,提高程序效率。

(3)主线程:每个Java程序至少有一个线程——main线程(主线程)。

(4)并发与并行:

二、创建线程方式1:继承Thread类

1.步骤

// 1. 继承 Thread 类 public class MyThread extends Thread { // 2. 重写 run() 方法(线程要执行的代码) @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("子线程:" + i); } } } // 3. 创建线程对象,调用 start() 启动 public class Test { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // 启动线程(会自动调用 run() 方法) // 主线程的代码 for (int i = 0; i < 20; i++) { System.out.println("主线程:" + i); } } }

ps:

start() 和 run() 的区别

start():启动新线程,新线程执行 run() 方法(正确)

run():直接调用,没有启动新线程,只是在当前线程执行(错误用法)

thread.run(); // 错误的!只是普通方法调用,没有新线程 thread.start(); // 正确的!启动新线程

三、多线程运行原理

1.线程调度

Java线程调度是抢占式的,不是时间片轮转

哪个线程抢到CPU时间片,哪个线程就执行

每次执行结果可能不同(不可预测)

// 多次运行结果可能不同 Thread t1 = new MyThread("线程1"); Thread t2 = new MyThread("线程2"); t1.start(); t2.start(); // 输出顺序每次可能不同

2.多线程内存原理

每个线程都有自己独立的栈空间

所有线程共享堆内存和方法区

四、Thread中常用方法

1.获取和设置线程名

public class MyThread extends Thread { @Override public void run() { // 获取当前线程名 String name = getName(); // 或:Thread.currentThread().getName() System.out.println(name + " 正在执行"); } } // 使用 MyThread t1 = new MyThread(); t1.setName("线程A"); // 设置线程名 t1.start(); MyThread t2 = new MyThread(); t2.setName("线程B"); t2.start();

2.获取当前线程

// 在任何位置获取当前正在执行的线程对象 Thread current = Thread.currentThread(); System.out.println("当前线程:" + current.getName());

3.sleep() 休眠

// 让当前线程暂停执行指定毫秒 System.out.println("开始"); Thread.sleep(3000); // 休眠3秒(需要处理InterruptedException) System.out.println("3秒后执行");

使用场景:模拟耗时操作、控制执行节奏

例子

// 倒计时 for (int i = 10; i >= 1; i--) { System.out.println(i); Thread.sleep(1000); // 每秒输出一个数 } System.out.println("发射!");

五、优先级、守护、礼让、插入线程

1.线程优先级

// 设置优先级(1-10,默认5) t1.setPriority(Thread.MIN_PRIORITY); // 1(最低) t2.setPriority(Thread.NORM_PRIORITY); // 5(默认) t3.setPriority(Thread.MAX_PRIORITY); // 10(最高) // 获取优先级 int priority = t1.getPriority();

注意:优先级高的线程抢占CPU的概率更高,但不保证一定先执行。

2.守护线程

// 守护线程:当所有非守护线程结束时,守护线程自动结束 // 如:垃圾回收线程(GC) Thread daemon = new MyThread(); daemon.setDaemon(true); // 设置为守护线程(必须在start之前设置) daemon.start();

特点:

守护线程为其他线程提供服务

JVM中只剩守护线程时,JVM退出

3.礼让线程(yield)

public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(getName() + ":" + i); Thread.yield(); // 礼让,暂停当前线程,让其他线程有机会执行 } } }

注意:yield() 只是"建议"调度器让出CPU,不保证一定礼让。

4.插入线程(join)

// join():等待该线程执行完毕,再继续执行当前线程 public class Test { public static void main(String[] args) throws InterruptedException { Thread t = new MyThread(); t.start(); for (int i = 0; i < 5; i++) { System.out.println("主线程:" + i); if (i == 2) { t.join(); // t线程插入,主线程等待t执行完毕 } } } }

六、创建线程方式2:实现Runnable接口

1. 步骤

// 1. 实现 Runnable 接口 public class MyRunnable implements Runnable { // 2. 重写 run() 方法 @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } // 3. 创建 Thread 对象,传入 Runnable,启动 public class Test { public static void main(String[] args) { MyRunnable runnable = new MyRunnable(); Thread t1 = new Thread(runnable, "线程A"); Thread t2 = new Thread(runnable, "线程B"); t1.start(); t2.start(); } }

2.Runnable 和 Thread 对比

推荐:实现 Runnable 接口(更灵活,符合面向接口编程)。

七、匿名内部类创建多线程

// 方式1:匿名内部类继承 Thread new Thread() { @Override public void run() { System.out.println("匿名Thread线程"); } }.start(); // 方式2:匿名内部类实现 Runnable new Thread(new Runnable() { @Override public void run() { System.out.println("匿名Runnable线程"); } }).start(); // Lambda 简化(JDK8+) new Thread(() -> { System.out.println("Lambda线程"); }).start();

八、线程安全问题

多个线程同时操作共享数据时,可能出现数据错乱。

// 卖票案例(线程不安全) public class Ticket implements Runnable { private int count = 100; // 共享的票数 @Override public void run() { while (count > 0) { // 模拟出票耗时 try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + "卖第" + count + "张票"); count--; } } } // 可能出现的问题: // 1. 卖出重复的票(两个线程读到了相同的count值) // 2. 卖出0张票或负数(count=1时两个线程都通过了count>0的判断)

九、同步代码块(解决线程安全)

1.synchronized 同步代码块

// 语法 synchronized(锁对象) { // 需要同步的代码(操作共享数据的代码) }

2.改进卖票案例

public class Ticket implements Runnable { private int count = 100; private Object lock = new Object(); // 锁对象 @Override public void run() { while (true) { synchronized (lock) { // 同步代码块 if (count > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + "卖第" + count + "张票"); count--; } else { break; } } } } }

3. 同步原理

线程A进入synchronized块 → 拿到锁 → 执行代码
线程B进入synchronized块 → 锁被占用 → 等待
线程A执行完毕 → 释放锁
线程B拿到锁 → 执行代码

关键:
- 同一把锁的同步代码块,同一时间只能有一个线程执行
- 同步保证了数据安全,但降低了效率

锁对象注意事项

多个线程必须使用同一个锁对象

锁对象可以是任意引用类型对象

通常用 new Object() 或 this 或 类名.class

十、同步方法

1.语法

public synchronized 返回值 方法名() { // 同步方法的锁对象 // 非静态方法:this // 静态方法:类名.class }

2.卖票案例(同步方法)

public class Ticket implements Runnable { private int count = 100; @Override public void run() { while (true) { if (!sellTicket()) break; // 卖完就退出 } } // 同步方法(锁对象是 this) public synchronized boolean sellTicket() { if (count > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName() + "卖第" + count + "张票"); count--; return true; } return false; } }

3.静态同步方法

public static synchronized void method() { // 锁对象是:类名.class(如 Ticket.class) }

十一、死锁

1.定义:两个或多个线程互相持有对方需要的锁,都在等待对方释放,导致永远阻塞。

// 死锁示例 public class DeadLock { static Object lockA = new Object(); static Object lockB = new Object(); public static void main(String[] args) { // 线程1:先拿lockA,再拿lockB new Thread(() -> { synchronized (lockA) { System.out.println("线程1:拿到lockA"); try { Thread.sleep(100); } catch (InterruptedException e) { } synchronized (lockB) { System.out.println("线程1:拿到lockB"); } } }).start(); // 线程2:先拿lockB,再拿lockA new Thread(() -> { synchronized (lockB) { System.out.println("线程2:拿到lockB"); try { Thread.sleep(100); } catch (InterruptedException e) { } synchronized (lockA) { System.out.println("线程2:拿到lockA"); } } }).start(); } } // 线程1持有lockA,等待lockB // 线程2持有lockB,等待lockA // → 互相等待,死锁!

2.如何避免死锁?

按照相同的顺序获取锁

使用 tryLock() 带超时的锁

减少锁的嵌套使用

十二、线程生命周期

1.线程的六种状态

2.状态说明

// 获取线程状态 Thread t = new MyThread(); System.out.println(t.getState()); // NEW t.start(); System.out.println(t.getState()); // RUNNABLE
http://www.gsyq.cn/news/1529011.html

相关文章:

  • 加油卡回收可行吗?深度拆解五种方式 - 猎卡网
  • 深入解析MPC8533E:PowerQUICC III核心寄存器配置与底层驱动实战
  • ArcMap 10.7/10.8闪退救星:一招清理Normal.mxt模板文件,90%问题秒解
  • 中国电子学会图形化2021.9月Scratch四级考级题
  • Visual C++运行库终极解决方案:一劳永逸的Windows系统必备神器
  • 免费解锁Wand专业功能终极指南:告别2小时限制,畅享完整游戏体验
  • 美团礼品卡回收实用指南 正规高价比平台推荐 - 购物卡回收找京尔回收
  • VLC点击暂停插件:3分钟学会终极观影控制技巧 [特殊字符]
  • 2026 金价高位反复波动,无锡闲置黄金最佳出手窗口期已现 - 奢侈品回收评测
  • HoRain云--React 列表 Keys
  • 掌握多尺度地理加权回归(MGWR):从数据到洞察的完整指南
  • 2026 郑州黄金回收核心门店地址指引:附近上门服务体系与耀辉全域覆盖优势 - 奢侈品回收
  • PXS20中断控制器:软件与硬件向量模式详解及嵌入式系统中断管理实战
  • 2026广安装修耐用又真实的材料攻略 - 装企自媒体训练营辉哥
  • 漫谈逆向工程
  • 2026年国内不锈钢螺旋焊管加工厂哪家强?不锈钢工业焊管厂家靠谱选择! - 资讯纵览
  • 2026易学入门App推荐榜:易学排盘软件怎么选?
  • GaussDB SQL JOIN避坑指南:从‘查不到数据’到‘查出重复数据’的常见错误分析与解决
  • 5个步骤让Windows资源管理器轻松预览3D模型文件:终极免费指南
  • 物联网智能锁赋能短租行业:身份核验与远程授权的全链路技术落地方案
  • 2026 无锡上门收金避坑:流动个人 vs 连锁门店上门,风险天差地别 - 奢侈品回收评测
  • 告别引脚短路!一文读懂PCB焊锡掩盖桥底层设计逻辑
  • 长沙天心区非遗餐馆 - 资讯快报
  • 告别报错:CAFE5分析中‘Failed to initialize’等常见错误的排查与解决思路
  • MCP协议:大模型上下文管理的工程化标准
  • 避开这3个坑,让你的dlnm模型更靠谱:R语言时间序列滞后建模实践指南
  • Seraphine:英雄联盟智能助手,5分钟掌握BP决策与战绩查询技巧
  • 深入解析FlexRay通信控制器:FIFO过滤与协议配置寄存器实战
  • [论文学习]重新思考大型语言模型忘却目标:梯度视角与超越
  • 2026更新东营市本地人必选的瓷砖空鼓专业维修公司TOP5推荐!卫生间空鼓翘边,厨房空鼓翘边,客厅空鼓翘边,全天响应,免费上门,6月专业瓷砖空鼓修复公司持证上岗师傅排名最新深度调研方案) - 一休咨询