JavaSE核心知识点03高级特性03-02(多线程)
🤟致敬读者
- 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉
📘博主相关
- 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息
文章目录
- JavaSE核心知识点03高级特性03-02(多线程)
- **一、多线程基础概念**
- **二、Java中创建线程的两种方式**
- **1. 继承Thread类**
- **2. 实现Runnable接口(推荐)**
- **三、线程的生命周期**
- **四、线程安全与同步**
- **1. 问题:多个线程共享数据导致竞态条件**
- **2. 解决方案:synchronized关键字**
- **3. 使用Lock接口(更灵活)**
- **4. volatile关键字**
- **五、线程间通信**
- **六、线程池(重点掌握)**
- **1. 为什么使用线程池?**
- **2. 使用Executors创建线程池**
- **3. 自定义线程池(推荐)**
- **七、常见问题与注意事项**
- **八、实战练习**
📃文章前言
- 🔷文章均为学习工作中整理的笔记。
- 🔶如有错误请指正,共同学习进步。
JavaSE核心知识点03高级特性03-02(多线程)
学习Java多线程是一个循序渐进的过程,下面我会从基础到应用为你详细讲解,并附上代码示例帮助你理解。
一、多线程基础概念
1. 什么是线程?
- 线程是程序执行的最小单位,一个进程可以包含多个线程,多个线程共享进程的内存资源。
- 多线程允许程序同时执行多个任务,提高资源利用率和响应速度。
类比:想象一家餐厅,一个厨师(单线程)同时处理多个订单会手忙脚乱,多个厨师(多线程)分工协作效率更高。
二、Java中创建线程的两种方式
1. 继承Thread类
public class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程执行: " + Thread.currentThread().getName());}public static void main(String[] args) {MyThread t1 = new MyThread();t1.start(); // 启动线程,JVM自动调用run()}
}
缺点:Java是单继承,继承Thread后无法继承其他类。
2. 实现Runnable接口(推荐)
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程执行: " + Thread.currentThread().getName());}public static void main(String[] args) {Thread t2 = new Thread(new MyRunnable());t2.start();}
}
优点:避免单继承限制,适合资源共享(如多个线程操作同一Runnable实例)。
三、线程的生命周期
线程的状态包括:
- 新建(New):创建未启动。
- 就绪(Runnable):调用
start()
后,等待CPU调度。 - 运行(Running):执行
run()
方法。 - 阻塞(Blocked):等待锁、I/O等资源。
- 终止(Terminated):
run()
执行完毕或异常退出。
四、线程安全与同步
1. 问题:多个线程共享数据导致竞态条件
public class Counter {private int count = 0;public void increment() {count++; // 非原子操作,可能被多个线程同时修改}
}
2. 解决方案:synchronized关键字
- 同步方法:锁住当前对象实例。
public synchronized void increment() {count++;
}
- 同步代码块:更细粒度控制。
public void increment() {synchronized(this) {count++;}
}
3. 使用Lock接口(更灵活)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Counter {private int count = 0;private Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock(); // 确保释放锁}}
}
4. volatile关键字
- 保证变量可见性,但不保证原子性。
private volatile boolean flag = false;
五、线程间通信
使用wait()
、notify()
、notifyAll()
实现线程协作(必须在同步块内调用)。
public class ProducerConsumer {private Queue<Integer> queue = new LinkedList<>();private int capacity = 5;public synchronized void produce(int item) throws InterruptedException {while (queue.size() == capacity) {wait(); // 队列满,等待}queue.add(item);notifyAll(); // 唤醒消费者}public synchronized int consume() throws InterruptedException {while (queue.isEmpty()) {wait(); // 队列空,等待}int item = queue.poll();notifyAll(); // 唤醒生产者return item;}
}
六、线程池(重点掌握)
1. 为什么使用线程池?
- 避免频繁创建/销毁线程的开销。
- 控制并发数量,防止资源耗尽。
2. 使用Executors创建线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo {public static void main(String[] args) {ExecutorService pool = Executors.newFixedThreadPool(3);for (int i = 0; i < 10; i++) {pool.execute(() -> {System.out.println(Thread.currentThread().getName() + " 执行任务");});}pool.shutdown(); // 关闭线程池}
}
3. 自定义线程池(推荐)
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.LinkedBlockingQueue;ThreadPoolExecutor executor = new ThreadPoolExecutor(5, // 核心线程数10, // 最大线程数60L, TimeUnit.SECONDS, // 空闲线程存活时间new LinkedBlockingQueue<>(100) // 任务队列
);
七、常见问题与注意事项
-
死锁:多个线程互相等待对方释放锁。
- 避免方法:按固定顺序获取锁,使用超时机制(
tryLock
)。
- 避免方法:按固定顺序获取锁,使用超时机制(
-
线程泄漏:线程未正确关闭导致资源耗尽。
- 解决:使用
try-finally
确保资源释放。
- 解决:使用
-
避免使用已废弃方法:
stop()
:强制终止线程,可能导致数据不一致。- 改用标志位控制线程退出:
public class SafeStop implements Runnable {private volatile boolean running = true;public void stop() {running = false;}@Overridepublic void run() {while (running) {// 执行任务}} }
八、实战练习
题目:模拟10个窗口卖100张票(线程安全)。
public class TicketSales implements Runnable {private int tickets = 100;private final Object lock = new Object();@Overridepublic void run() {while (true) {synchronized (lock) {if (tickets <= 0) break;System.out.println(Thread.currentThread().getName() + " 卖出第 " + tickets-- + " 张票");}try {Thread.sleep(100); // 模拟操作耗时} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {TicketSales task = new TicketSales();for (int i = 0; i < 10; i++) {new Thread(task, "窗口" + (i+1)).start();}}
}
通过以上内容,你应该对Java多线程有了全面的认识。学习时建议多写代码调试,观察不同情况下的输出,逐步理解线程的行为。遇到问题可以查阅Java官方文档或可靠的教程资源。
📜文末寄语
- 🟠关注我,获取更多内容。
- 🟡技术动态、实战教程、问题解决方案等内容持续更新中。
- 🟢《全栈知识库》技术交流和分享社区,集结全栈各领域开发者,期待你的加入。
- 🔵加入开发者的《专属社群》,分享交流,技术之路不再孤独,一起变强。
- 🟣点击下方名片获取更多内容🍭🍭🍭👇