Java 多线程编程介绍
Java学习资料
Java学习资料
Java学习资料
一、引言
在现代软件开发中,处理复杂的任务和提高程序的性能是至关重要的。单线程程序一次只能执行一个任务,在处理耗时操作时会导致程序响应缓慢。Java 多线程编程为解决这类问题提供了有效的方案。通过多线程,程序可以同时执行多个任务,充分利用多核处理器的计算能力,提高程序的并发性和响应速度。
二、线程的基本概念
2.1 线程与进程
进程:是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。每个进程都有自己独立的内存空间和系统资源。例如,当我们打开一个浏览器,就启动了一个浏览器进程。
线程:是进程中的一个执行单元,是 CPU 调度和分派的基本单位。一个进程可以包含多个线程,这些线程共享进程的内存空间和系统资源。比如在浏览器进程中,可以有一个线程负责渲染页面,另一个线程负责下载文件。
2.2 线程的状态
Java 中的线程有六种状态,定义在 Thread.State 枚举中:
新建(NEW):当创建一个 Thread 对象时,线程处于新建状态,此时线程还没有开始执行。
就绪(RUNNABLE):调用线程的 start() 方法后,线程进入就绪状态,等待 CPU 调度执行。
运行(Running):当 CPU 分配时间片给该线程时,线程进入运行状态,开始执行 run() 方法中的代码。
阻塞(BLOCKED):线程在获取同步锁失败、等待 I/O 操作完成等情况下会进入阻塞状态,暂时停止执行。
等待(WAITING):线程调用 Object.wait()、Thread.join() 等方法后会进入等待状态,需要其他线程唤醒。
超时等待(TIMED_WAITING):与等待状态类似,但可以指定等待的时间,超时后线程会自动唤醒,例如调用 Thread.sleep(long millis) 方法。
终止(TERMINATED):线程的 run() 方法执行完毕或者因为异常退出,线程进入终止状态。
三、创建线程的方式
3.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);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadInheritanceExample {
public static void main(String[] args) {
MyThread thread1 = new MyThread();
MyThread thread2 = new MyThread();
thread1.start();
thread2.start();
}
}
3.2 实现 Runnable 接口
实现 Runnable 接口的 run() 方法,然后将实现类的实例作为参数传递给 Thread 类的构造函数。示例代码如下:
@Override
class MyRunnable implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableExample {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.start();
thread2.start();
}
}
3.3 实现 Callable 接口
Callable 接口与 Runnable 接口类似,但 Callable 的 call() 方法可以有返回值,并且可以抛出异常。通常与 Future 接口配合使用来获取线程执行的结果。示例代码如下:
import java.util.concurrent.*;
class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += i;
}
return sum;
}
}
public class CallableExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
MyCallable myCallable = new MyCallable();
Future<Integer> future = executor.submit(myCallable);
Integer result = future.get();
System.out.println("Result: " + result);
executor.shutdown();
}
}
四、线程同步与并发控制
4.1 同步方法和同步块
当多个线程访问共享资源时,可能会出现数据不一致的问题,这就需要进行线程同步。Java 提供了 synchronized 关键字来实现线程同步。
同步方法:在方法声明中使用 synchronized 关键字,示例代码如下:
class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
同步块:使用 synchronized 关键字修饰代码块,指定同步的对象,示例代码如下:
class Counter {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
count++;
}
}
public int getCount() {
return count;
}
}
4.2 ReentrantLock
ReentrantLock 是 Java 提供的一个可重入锁,与 synchronized 关键字类似,但功能更强大。示例代码如下:
import java.util.concurrent.locks.ReentrantLock;
class Counter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
4.3 wait()、notify() 和 notifyAll()
这三个方法是 Object 类的方法,用于线程间的协作。wait() 方法使当前线程进入等待状态,notify() 方法唤醒在此对象监视器上等待的单个线程,notifyAll() 方法唤醒在此对象监视器上等待的所有线程。示例代码如下:
class SharedResource {
private boolean flag = false;
public synchronized void waitForFlag() throws InterruptedException {
while (!flag) {
wait();
}
System.out.println("Flag is set, continuing...");
}
public synchronized void setFlag() {
flag = true;
notify();
}
}
五、线程池
线程池是一种管理线程的机制,它可以预先创建一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务,任务执行完毕后线程不会销毁,而是返回到线程池中等待下一个任务。使用线程池可以减少线程创建和销毁的开销,提高程序的性能。Java 提供了 ExecutorService 接口和 Executors 工厂类来创建和管理线程池。示例代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class Task implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is executing task.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.submit(new Task());
}
executor.shutdown();
}
}
六、总结
Java 多线程编程是提高程序性能和响应速度的重要手段。通过创建线程、进行线程同步和并发控制以及使用线程池等技术,我们可以充分发挥多核处理器的优势,开发出高效、稳定的多线程应用程序。但同时,多线程编程也带来了一些问题,如线程安全、死锁等,需要开发者在编程过程中谨慎处理。