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. 思考在前端开发中遇到的并发场景
相关推荐
云心雨禅10 分钟前
Spring Boot热更新技巧:节省90%重启时间
java·数据库·spring boot
岁忧15 分钟前
(LeetCode 每日一题) 2966. 划分数组并满足最大差限制 (贪心、排序)
java·c++·算法·leetcode·职场和发展·go
Maỿbe26 分钟前
实现回显服务器(基于UDP)
java·javaweb·echo·回显服务器
葡萄城技术团队30 分钟前
450 + 公式计算支持,GcExcel Java 强大计算引擎揭秘
java
lifallen37 分钟前
Java BitSet类解析:高效位向量实现
java·开发语言·后端·算法
用户0595661192091 小时前
java 最新技术实操内容:从基础到进阶的全方位指南
java·架构·编程语言
MyFreeIT2 小时前
Unable to start embedded Tomcat
java·tomcat·mybatis
风一样的树懒2 小时前
Zuul动态路由黑洞揭秘:每秒10万并发的刷新策略
java
一只帆記2 小时前
Java 实现后端调用 Chromium 浏览器无头模式截图的方案
java·开发语言
知月玄2 小时前
网页后端开发(基础2--maven单元测试)
java·开发语言