学习时间: 4-5小时
学习目标: 掌握Java多线程编程基础,理解并发概念和线程安全
📋 详细学习清单
✅ 第一部分:多线程概念理解(90分钟)
1. 多线程与JavaScript对比
JavaScript (你熟悉的异步编程)
javascript
// JavaScript 单线程 + 事件循环
console.log("开始");
// 异步操作 - setTimeout
setTimeout(() => {
console.log("延时操作完成");
}, 1000);
// Promise异步
fetch('/api/data')
.then(response => response.json())
.then(data => console.log("数据加载完成"))
.catch(error => console.error("请求失败"));
// async/await
async function loadData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
console.log("异步数据:", data);
} catch (error) {
console.error("加载失败:", error);
}
}
console.log("结束");
// 输出顺序: 开始 -> 结束 -> 延时操作完成 -> 数据加载完成
Java (今天学习的多线程)
java
// Java 真正的多线程并发执行
public class ThreadBasics {
public static void main(String[] args) {
System.out.println("主线程开始 - " + Thread.currentThread().getName());
// 方法1: 继承Thread类
class MyThread extends Thread {
private String taskName;
public MyThread(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.println(taskName + " - 执行第" + i + "次 - 线程:" +
Thread.currentThread().getName());
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(taskName + " 完成!");
}
}
// 创建并启动线程
MyThread thread1 = new MyThread("任务A");
MyThread thread2 = new MyThread("任务B");
thread1.start(); // 启动线程1
thread2.start(); // 启动线程2
System.out.println("主线程继续执行其他任务...");
// 等待子线程完成
try {
thread1.join(); // 等待thread1完成
thread2.join(); // 等待thread2完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("所有任务完成!");
}
}
2. 线程生命周期详解
java
public class ThreadLifecycle {
public static void main(String[] args) {
System.out.println("=== 线程生命周期演示 ===");
Thread worker = new Thread(() -> {
System.out.println("1. RUNNABLE: 线程正在运行");
try {
System.out.println("2. TIMED_WAITING: 线程休眠2秒");
Thread.sleep(2000);
System.out.println("3. RUNNABLE: 线程恢复运行");
// 模拟同步代码块,可能进入BLOCKED状态
synchronized(ThreadLifecycle.class) {
System.out.println("4. 在同步代码块中执行");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("线程被中断");
Thread.currentThread().interrupt();
}
System.out.println("5. TERMINATED: 线程即将结束");
});
System.out.println("线程状态: " + worker.getState()); // NEW
worker.start();
System.out.println("线程状态: " + worker.getState()); // RUNNABLE
try {
Thread.sleep(500);
System.out.println("线程状态: " + worker.getState()); // TIMED_WAITING
worker.join(); // 等待线程完成
System.out.println("线程状态: " + worker.getState()); // TERMINATED
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
✅ 第二部分:实现多线程的方式(60分钟)
1. 三种创建线程的方法
java
public class ThreadCreationMethods {
// 方法1: 继承Thread类
static class ThreadByExtending extends Thread {
private String taskName;
public ThreadByExtending(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(taskName + " - 继承Thread - 第" + i + "次执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
// 方法2: 实现Runnable接口
static class TaskByRunnable implements Runnable {
private String taskName;
public TaskByRunnable(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
System.out.println(taskName + " - 实现Runnable - 第" + i + "次执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
public static void main(String[] args) {
System.out.println("=== 三种创建线程的方法 ===");
// 方法1: 继承Thread
ThreadByExtending thread1 = new ThreadByExtending("任务1");
thread1.start();
// 方法2: 实现Runnable接口
TaskByRunnable task2 = new TaskByRunnable("任务2");
Thread thread2 = new Thread(task2);
thread2.start();
// 方法3: 使用Lambda表达式(推荐)
Thread thread3 = new Thread(() -> {
for (int i = 1; i <= 3; i++) {
System.out.println("任务3 - Lambda表达式 - 第" + i + "次执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
thread3.start();
// 等待所有线程完成
try {
thread1.join();
thread2.join();
thread3.join();
System.out.println("所有线程执行完毕!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2. 线程池的使用
java
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
System.out.println("=== 线程池使用示例 ===");
// 创建固定大小的线程池
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交多个任务
for (int i = 1; i <= 10; i++) {
final int taskNumber = i;
executor.submit(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("任务" + taskNumber + " 开始执行 - 线程: " + threadName);
try {
// 模拟任务执行时间
Thread.sleep((long)(Math.random() * 3000 + 1000));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("任务" + taskNumber + " 执行完成 - 线程: " + threadName);
});
}
// 关闭线程池
executor.shutdown();
try {
// 等待所有任务完成,最多等待10秒
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
System.out.println("强制关闭线程池");
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
System.out.println("所有任务执行完毕!");
}
}
✅ 第三部分:线程同步与安全(80分钟)
1. 线程安全问题演示
java
public class ThreadSafetyProblem {
private static int counter = 0;
// 不安全的计数器
public static void unsafeIncrement() {
counter++; // 这个操作不是原子性的!
}
// 安全的计数器 - 使用synchronized
public static synchronized void safeIncrement() {
counter++;
}
public static void main(String[] args) {
System.out.println("=== 线程安全问题演示 ===");
// 演示线程不安全的情况
counter = 0;
Thread[] threads = new Thread[10];
// 创建10个线程,每个线程执行1000次不安全的自增
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
unsafeIncrement();
}
});
threads[i].start();
}
// 等待所有线程完成
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("不安全自增结果: " + counter + " (期望值: 10000)");
// 演示线程安全的情况
counter = 0;
for (int i = 0; i < 10; i++) {
threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
safeIncrement();
}
});
threads[i].start();
}
// 等待所有线程完成
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("安全自增结果: " + counter + " (期望值: 10000)");
}
}
2. 同步机制详解
java
public class SynchronizationExample {
private final Object lock = new Object();
private int sharedResource = 0;
// 方法级同步
public synchronized void synchronizedMethod() {
System.out.println("同步方法执行 - 线程: " + Thread.currentThread().getName());
sharedResource++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 代码块同步
public void synchronizedBlock() {
synchronized(lock) {
System.out.println("同步代码块执行 - 线程: " + Thread.currentThread().getName());
sharedResource++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
// 静态同步方法
public static synchronized void staticSynchronizedMethod() {
System.out.println("静态同步方法执行 - 线程: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
SynchronizationExample example = new SynchronizationExample();
System.out.println("=== 同步机制演示 ===");
// 测试方法级同步
Thread t1 = new Thread(() -> example.synchronizedMethod());
Thread t2 = new Thread(() -> example.synchronizedMethod());
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("共享资源值: " + example.sharedResource);
}
}
✅ 第四部分:实战练习(50分钟)
1. 生产者消费者模式
java
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumerExample {
private static final int CAPACITY = 5;
private Queue<Integer> buffer = new LinkedList<>();
private final Object lock = new Object();
// 生产者
class Producer implements Runnable {
@Override
public void run() {
int value = 0;
while (true) {
synchronized (lock) {
while (buffer.size() == CAPACITY) {
try {
System.out.println("缓冲区已满,生产者等待...");
lock.wait(); // 等待消费者消费
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
buffer.offer(value);
System.out.println("生产者生产: " + value + ", 缓冲区大小: " + buffer.size());
value++;
lock.notifyAll(); // 通知消费者
}
try {
Thread.sleep(1000); // 生产间隔
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
}
// 消费者
class Consumer implements Runnable {
private String name;
public Consumer(String name) {
this.name = name;
}
@Override
public void run() {
while (true) {
synchronized (lock) {
while (buffer.isEmpty()) {
try {
System.out.println("缓冲区为空," + name + " 等待...");
lock.wait(); // 等待生产者生产
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
int value = buffer.poll();
System.out.println(name + " 消费: " + value + ", 缓冲区大小: " + buffer.size());
lock.notifyAll(); // 通知生产者
}
try {
Thread.sleep(1500); // 消费间隔
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
}
public static void main(String[] args) {
ProducerConsumerExample example = new ProducerConsumerExample();
// 启动生产者和消费者
Thread producer = new Thread(example.new Producer());
Thread consumer1 = new Thread(example.new Consumer("消费者1"));
Thread consumer2 = new Thread(example.new Consumer("消费者2"));
producer.start();
consumer1.start();
consumer2.start();
// 运行10秒后停止
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
producer.interrupt();
consumer1.interrupt();
consumer2.interrupt();
}
}
2. 银行转账并发安全示例
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BankAccountExample {
static class BankAccount {
private final String accountNumber;
private double balance;
private final Lock lock = new ReentrantLock();
public BankAccount(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
// 安全的转账方法
public boolean transfer(BankAccount target, double amount) {
// 避免死锁:按照账户号排序获取锁
BankAccount firstLock = this.accountNumber.compareTo(target.accountNumber) < 0 ? this : target;
BankAccount secondLock = this.accountNumber.compareTo(target.accountNumber) < 0 ? target : this;
firstLock.lock.lock();
try {
secondLock.lock.lock();
try {
if (this.balance >= amount) {
this.balance -= amount;
target.balance += amount;
System.out.println(Thread.currentThread().getName() +
" - 从账户 " + this.accountNumber +
" 转账 " + amount + " 到账户 " + target.accountNumber);
System.out.println("转账后余额 - " + this.accountNumber + ": " + this.balance +
", " + target.accountNumber + ": " + target.balance);
return true;
} else {
System.out.println("余额不足,转账失败");
return false;
}
} finally {
secondLock.lock.unlock();
}
} finally {
firstLock.lock.unlock();
}
}
public double getBalance() {
lock.lock();
try {
return balance;
} finally {
lock.unlock();
}
}
public String getAccountNumber() {
return accountNumber;
}
}
public static void main(String[] args) {
BankAccount account1 = new BankAccount("A001", 1000);
BankAccount account2 = new BankAccount("A002", 1000);
System.out.println("=== 银行转账并发安全示例 ===");
System.out.println("初始余额 - A001: " + account1.getBalance() + ", A002: " + account2.getBalance());
// 创建多个转账线程
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
account1.transfer(account2, 100);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "线程1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
account2.transfer(account1, 150);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "线程2");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("\n最终余额 - A001: " + account1.getBalance() + ", A002: " + account2.getBalance());
System.out.println("总金额: " + (account1.getBalance() + account2.getBalance()) + " (应该等于2000)");
}
}
📝 今日学习总结
✅ 今日完成内容
- 多线程基础概念 - 理解并发与并行
- 线程创建方式 - Thread类、Runnable接口、Lambda表达式
- 线程生命周期 - NEW、RUNNABLE、BLOCKED、WAITING、TERMINATED
- 线程池使用 - ExecutorService的基本用法
- 线程同步机制 - synchronized关键字、Lock接口
- 实战练习 - 生产者消费者模式、银行转账安全
🎯 重点掌握
- 多线程与JavaScript异步的区别
- synchronized的使用场景
- 线程安全问题的解决方案
- 避免死锁的策略
📚 明日预告
第7天将学习:
- 泛型(Generics)深入
- 反射(Reflection)机制
- 注解(Annotations)使用
- 枚举(Enum)类型
💡 练习建议
- 运行所有示例代码,观察输出结果
- 尝试修改线程数量,观察并发效果
- 实现一个简单的下载器,支持多线程并发下载
- 思考在前端开发中遇到的并发场景