java学习笔记——多线程
2026/6/15 14:06:50 网站建设 项目流程

一、多线程基础知识

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询