当前位置: 首页 > 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.jsqmd.com/news/1017189/

相关文章:

  • 深入解析MPC8533E:PowerQUICC III核心寄存器配置与底层驱动实战
  • 加油卡回收可行吗?深度拆解五种方式 - 猎卡网
  • 2026年硕士毕业论文AI测评:全流程覆盖,5款工具推荐
  • 中国电子学会图形化2021.9月Scratch四级考级题
  • 避坑指南:Redis GEO在Spring Boot中计算距离的3个常见错误与正确姿势
  • 2026年搜索众智商学院联系方式时怎样避开非官方信息?课程咨询渠道识别指南 - 众智商学院职业教育
  • 2026年人事业财生产一体化实战手册;无锡钉钉数字化管理系统选型指南: - 优质企业观察收录
  • MSC711x DSP系统性能调优:内存访问与DMA传输优化实战指南
  • ArcMap 10.7/10.8闪退救星:一招清理Normal.mxt模板文件,90%问题秒解
  • Visual C++运行库终极解决方案:一劳永逸的Windows系统必备神器
  • 网络分类:局域网、城域网、广域网、互联网
  • WarcraftHelper:魔兽争霸III终极性能优化与兼容性修复完全指南
  • 免费解锁Wand专业功能终极指南:告别2小时限制,畅享完整游戏体验
  • Windows系统优化新方案:智能清理“此电脑“顽固快捷方式的终极工具
  • 美团礼品卡回收实用指南 正规高价比平台推荐 - 购物卡回收找京尔回收
  • 深入解析YOLOv9:可编程梯度信息引领的信息瓶颈破解之道 —— 完整原理、实现与部署指南
  • 2026年五大有实力的电磁溢流阀专业加工品牌对比清单 - 资讯纵览
  • 2026 金价高位反复波动,无锡闲置黄金最佳出手窗口期已现 - 奢侈品回收评测
  • VLC点击暂停插件:3分钟学会终极观影控制技巧 [特殊字符]
  • LOL切回桌面问题,采用监控抓出元凶方式
  • HoRain云--React 列表 Keys
  • MPC866 PowerQUICC处理器核心架构与指令集深度解析
  • ChatGPT 5.5 怎么用在日常开发里?我总结了 6 个最实用场景
  • 掌握多尺度地理加权回归(MGWR):从数据到洞察的完整指南
  • 2026年众智商学院课程咨询怎么确认?正确查询官网和联系电话的方法 - 众智商学院官方
  • 如何注销自己的营业执照?营业执照注销攻略来了! - 慧办好
  • 深入解析FlexPWM:从基础原理到电机驱动实战应用
  • 2026驻马店建材行业,哪家做短视频代运营比较靠谱? - 年度推荐企业名录
  • 2026 郑州黄金回收核心门店地址指引:附近上门服务体系与耀辉全域覆盖优势 - 奢侈品回收
  • 中国电子学会图形化2021.3月Scratch四级考级题