Java学习第6天 - 多线程编程基础

学习时间: 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)");
    }
}

📝 今日学习总结

✅ 今日完成内容

  1. 多线程基础概念 - 理解并发与并行
  2. 线程创建方式 - Thread类、Runnable接口、Lambda表达式
  3. 线程生命周期 - NEW、RUNNABLE、BLOCKED、WAITING、TERMINATED
  4. 线程池使用 - ExecutorService的基本用法
  5. 线程同步机制 - synchronized关键字、Lock接口
  6. 实战练习 - 生产者消费者模式、银行转账安全

🎯 重点掌握

  • 多线程与JavaScript异步的区别
  • synchronized的使用场景
  • 线程安全问题的解决方案
  • 避免死锁的策略

📚 明日预告

第7天将学习:

  • 泛型(Generics)深入
  • 反射(Reflection)机制
  • 注解(Annotations)使用
  • 枚举(Enum)类型

💡 练习建议

  1. 运行所有示例代码,观察输出结果
  2. 尝试修改线程数量,观察并发效果
  3. 实现一个简单的下载器,支持多线程并发下载
  4. 思考在前端开发中遇到的并发场景
相关推荐
侠客行03175 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪5 小时前
深入浅出LangChain4J
java·langchain·llm
老毛肚7 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
风流倜傥唐伯虎7 小时前
Spring Boot Jar包生产级启停脚本
java·运维·spring boot
Yvonne爱编码7 小时前
JAVA数据结构 DAY6-栈和队列
java·开发语言·数据结构·python
Re.不晚7 小时前
JAVA进阶之路——无奖问答挑战1
java·开发语言
你这个代码我看不懂7 小时前
@ConditionalOnProperty不直接使用松绑定规则
java·开发语言
fuquxiaoguang7 小时前
深入浅出:使用MDC构建SpringBoot全链路请求追踪系统
java·spring boot·后端·调用链分析
琹箐8 小时前
最大堆和最小堆 实现思路
java·开发语言·算法
__WanG8 小时前
JavaTuples 库分析
java