Java 多线程详解
目录
1. 引言
2. 线程的基础知识
2.1 线程与进程的区别
2.2 线程的生命周期
3. 创建线程的三种方式
3.1 继承 Thread 类
优缺点
3.2 实现 Runnable 接口
优缺点
3.3 使用 Callable 和 Future
4. 线程同步与安全
4.1 线程安全问题
4.2 使用 synchronized
4.3 使用 ReentrantLock
5. 高级多线程
5.1 线程池
5.2 并发工具类
1. CountDownLatch:等待多个线程完成任务。
6. 常见问题与优化
7. 结论
1. 引言
在现代开发中,多线程是一个关键技术。无论是服务器处理并发请求,还是在客户端实现流畅的用户体验,多线程技术都能发挥重要作用。本篇文章将从基础概念到高级应用,全面解析 Java 多线程,帮助你深入理解并应用。
2. 线程的基础知识
2.1 线程与进程的区别
- 进程是资源分配的最小单位,彼此独立;
- 线程是 CPU 调度的最小单位,同一进程中的线程共享内存和资源。
资源共享 | 独立内存空间 | 同一进程内共享 |
开销 | 创建成本高 | 较低 |
通信 | IPC(如管道) | 直接共享变量 |
2.2 线程的生命周期
线程在 Java 中有五个状态:
- 新建 (New):
Thread t = new Thread();
- 就绪 (Runnable):调用
start()
后,等待 CPU 调度。- 运行 (Running):获得 CPU 时间片后执行。
- 阻塞 (Blocked):等待资源或信号。
- 终止 (Terminated):线程任务完成或被中断。
生命周期图:
New -> Runnable -> Running -> Terminated
^ |
| v
Blocked <-
3. 创建线程的三种方式
3.1 继承 Thread
类
通过继承 Thread
类并重写 run()
方法:
class MyThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running");
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
优缺点
- 优点:简单,代码清晰。
- 缺点:Java 不支持多继承,限制了扩展性。
3.2 实现
Runnable
接口通过实现
Runnable
接口来创建线程,避免继承限制:class MyRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running"); } } public class Main { public static void main(String[] args) { Thread t1 = new Thread(new MyRunnable()); t1.start(); } }
优缺点
- 优点:更灵活,可以继承其他类。
- 缺点:相比直接继承
Thread
,稍微复杂。
3.3 使用 Callable
和 Future
与 Runnable
不同,Callable
可以返回结果并抛出异常:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 123;
}
}
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> future = new FutureTask<>(new MyCallable());
Thread t = new Thread(future);
t.start();
System.out.println("Result: " + future.get());
}
}
4. 线程同步与安全
4.1 线程安全问题
多个线程同时修改共享变量可能导致数据不一致:
class Counter {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
for (int i = 0; i < 1000; i++) {
new Thread(counter::increment).start();
}
System.out.println("Final count: " + counter.getCount());
}
}
4.2 使用 synchronized
解决线程安全问题的基本方法:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
4.3 使用 ReentrantLock
更灵活的锁机制:
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
5. 高级多线程
5.1 线程池
使用 ExecutorService
创建线程池:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.submit(() -> {
System.out.println(Thread.currentThread().getName() + " is running");
});
}
executor.shutdown();
}
}
5.2 并发工具类
1. CountDownLatch
:等待多个线程完成任务。
import java.util.concurrent.CountDownLatch;
public class Main {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " finished");
latch.countDown();
}).start();
}
latch.await();
System.out.println("All threads finished");
}
}
2. CyclicBarrier
:同步多个线程。
import java.util.concurrent.CyclicBarrier;
public class Main {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("All threads reached the barrier");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " is waiting");
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
6. 常见问题与优化
- 死锁:
- 避免锁的嵌套。
- 按顺序获取锁。
- 性能问题:
- 减少锁粒度。
- 使用并发容器,如
ConcurrentHashMap
。- 调试技巧:
- 使用工具如 VisualVM 监控线程状态。
7. 结论
Java 多线程是开发者必备技能。通过本文的学习,你将能够创建线程、解决线程安全问题,并灵活运用线程池和并发工具类。记住,线程的强大伴随着复杂性,需要谨慎设计。欢迎大家评论区留言交流,希望文章对大家有帮助!!