1109 字
6 分钟
多线程编程
Java 多线程编程详解
在 Java 中,多线程编程是一种允许多个线程并发执行任务的机制。多线程编程可以提高程序的效率,特别是在处理大量计算或 I/O 操作时。
一、多线程基础概念
1. 线程和进程
- 进程:是程序在操作系统中执行的一个实例,具有独立的内存空间。
- 线程:是进程的一个执行单元,一个进程可以有多个线程,它们共享进程的内存空间。
2. 多线程的优势
- 提高CPU利用率:多线程可以让 CPU 在等待 I/O 操作时继续执行其他线程的任务。
- 改善程序的响应性:在 GUI 程序中,多线程可以防止用户界面卡顿。
- 提高计算密集型任务的性能:可以并行执行多个计算任务,特别是在多核处理器上。
3. Java 中的线程状态
Java 线程有 6 种主要状态:
- New:线程对象被创建,但还未启动。
- Runnable:线程准备运行,并等待 CPU 调度。
- Blocked:线程在等待锁释放。
- Waiting:线程在等待另一个线程执行特定的动作。
- Timed Waiting:线程在等待指定的时间。
- Terminated:线程完成了任务或因异常而终止。
二、Java 中创建多线程的三种方式
1. 继承 Thread
类
这是创建线程的最简单方法,直接继承 Thread
类并重写 run()
方法。
class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}
}
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.start(); // 启动线程
t2.start();
}
}
优点:
- 简单直观。
缺点:
- Java 只支持单继承,继承
Thread
后无法继承其他类。
2. 实现 Runnable
接口
相比继承 Thread
类,实现 Runnable
接口更加灵活,因为可以实现多个接口。
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " - " + i);
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
Thread t2 = new Thread(myRunnable);
t1.start(); // 启动线程
t2.start();
}
}
优点:
- 灵活,可以继承其他类。
- 更适合资源共享。
缺点:
- 稍微复杂,需要创建
Thread
实例。
3. 使用 Callable
和 Future
Callable
接口与 Runnable
类似,但它可以返回结果或抛出异常。使用 ExecutorService
执行并获取线程的返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += i;
System.out.println(Thread.currentThread().getName() + " - Sum: " + sum);
}
return sum;
}
}
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<Integer> result1 = executor.submit(new MyCallable());
Future<Integer> result2 = executor.submit(new MyCallable());
try {
System.out.println("Result 1: " + result1.get());
System.out.println("Result 2: " + result2.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}
优点:
- 可以返回结果。
- 可以抛出异常。
缺点:
- 需要使用
ExecutorService
来管理线程。
三、线程同步
当多个线程同时访问共享资源时,可能会发生数据不一致的问题。为了避免这种情况,Java 提供了线程同步机制。
1. synchronized
关键字
synchronized
可以用于方法或代码块,确保同一时间只有一个线程可以访问某个方法或代码块。
同步方法:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
System.out.println(Thread.currentThread().getName() + " - Count: " + count);
}
}
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
counter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
2. Lock
接口
Lock
接口提供了比 synchronized
更加灵活的锁定机制。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
System.out.println(Thread.currentThread().getName() + " - Count: " + count);
} finally {
lock.unlock();
}
}
}
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
counter.increment();
}
};
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
}
}
四、线程池
创建大量线程会消耗系统资源。Java 提供了 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.execute(() -> {
System.out.println(Thread.currentThread().getName() + " - Task");
});
}
executor.shutdown();
}
}