编程自学指南:java程序设计开发,多线程编程,为什么需要多线程?线程的创建与启动,线程同步与锁机制,线程池
编程自学指南:java程序设计开发,多线程编程
学习目标:
-
理解进程与线程的核心概念及区别
-
掌握Java中线程的创建与生命周期管理
-
能够通过同步机制解决线程安全问题
-
使用线程池优化多线程程序性能
一、课程引入
1.1 为什么需要多线程?
-
应用场景:
-
提高程序响应速度(如GUI界面后台任务)
-
充分利用多核CPU资源
-
实现异步处理(如文件下载、网络请求)
-
-
生活类比:
-
进程:银行营业厅(独立资源单位)
-
线程:银行窗口(共享进程资源,并发处理任务)
-
二、线程的创建与启动
2.1 继承Thread类
案例1:简单线程执行
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行:" + Thread.currentThread().getName());
}
}
// 启动线程
MyThread thread = new MyThread();
thread.start(); // 输出:线程执行:Thread-0
2.2 实现Runnable接口(推荐)
案例2:多窗口售票系统
public class Ticket implements Runnable {
private int tickets = 10;
@Override
public void run() {
while (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "售出票号:" + tickets--);
}
}
}
// 启动三个窗口
Thread t1 = new Thread(new Ticket(), "窗口1");
Thread t2 = new Thread(new Ticket(), "窗口2");
t1.start();
t2.start();
// 注意:此处存在线程安全问题(后续解决)
三、线程同步与锁机制
3.1 synchronized关键字
案例3:解决售票超卖问题
public class SafeTicket implements Runnable {
private int tickets = 10;
@Override
public void run() {
while (true) {
synchronized (this) { // 同步代码块
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "售出票号:" + tickets--);
} else {
break;
}
}
}
}
}
3.2 Lock接口(ReentrantLock)
案例4:使用Lock实现同步
private Lock lock = new ReentrantLock();
public void run() {
while (true) {
lock.lock();
try {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "售出票号:" + tickets--);
} else {
break;
}
} finally {
lock.unlock();
}
}
}
3.3 volatile关键字
-
作用:确保变量可见性,禁止指令重排序
private volatile boolean running = true;
public void run() {
while (running) {
// 执行任务
}
}
public void stop() {
running = false;
}
四、线程池
4.1 Executor框架
案例5:使用线程池执行任务
ExecutorService pool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
pool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "执行任务");
});
}
pool.shutdown();
4.2 Callable与Future
案例6:获取异步计算结果
Callable<Integer> task = () -> {
Thread.sleep(1000);
return 1 + 1;
};
ExecutorService pool = Executors.newSingleThreadExecutor();
Future<Integer> future = pool.submit(task);
System.out.println("计算结果:" + future.get()); // 阻塞直到结果返回
pool.shutdown();
五、综合应用
5.1 案例7:生产者-消费者模型
public class MessageQueue {
private Queue<String> queue = new LinkedList<>();
private int capacity = 5;
public synchronized void produce(String msg) throws InterruptedException {
while (queue.size() == capacity) {
wait(); // 队列满时等待
}
queue.offer(msg);
notifyAll(); // 唤醒消费者
}
public synchronized String consume() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 队列空时等待
}
String msg = queue.poll();
notifyAll(); // 唤醒生产者
return msg;
}
}
六、常见问题与最佳实践
6.1 常见错误
-
错误1:直接调用run()方法
thread.run(); // 错误:不会启动新线程,仍在主线程执行
-
错误2:锁对象选择不当
synchronized (new Object()) { ... } // 锁无效!
6.2 最佳实践
-
优先实现Runnable接口:避免单继承限制
-
使用线程池代替手动创建线程:资源可控,避免频繁创建销毁开销
-
避免死锁:按固定顺序获取多个锁
七、总结与练习
7.1 总结
-
线程创建:继承Thread vs 实现Runnable/Callable
-
线程同步:synchronized、Lock、volatile
-
线程池:Executor框架管理线程生命周期
7.2 课后任务
-
实现一个多线程下载器(模拟分块下载文件)
-
用线程池优化案例2的售票系统
-
预习下一节课:网络编程基础
7.3 扩展挑战
-
用
CompletableFuture
实现异步任务链(如先查询用户信息,再查询订单)