目录
1. 引言
2. 线程的创建与管理
2.1 继承 Thread 类
2.2 实现 Runnable 接口
2.3 实现 Callable 接口
3. 线程的生命周期
4. 线程同步
4.1 synchronized 关键字
4.2 ReentrantLock
5. 线程池
5.1 使用 ExecutorService 创建线程池
6. 并发工具类
6.1 CountDownLatch
7. 线程创建方式对比
8. 结论
Java 多线程与并发编程
1. 引言
多线程与并发编程是 Java 中的一个重要特性,允许应用程序同时执行多个任务,充分利用多核处理器的性能,从而提高程序的效率和响应能力。在现代应用程序中,多线程和并发几乎是必不可少的,无论是处理用户请求、管理后台任务,还是处理大规模数据运算。本篇文章将深入探讨 Java 中多线程与并发编程的基础知识,包括线程的创建、线程的生命周期、线程同步、线程池、并发工具类等内容,结合代码示例帮助您全面掌握这些概念。
2. 线程的创建与管理
Java 提供了三种主要的方式来创建线程:继承 Thread
类、实现 Runnable
接口、实现 Callable
接口。
2.1 继承 Thread 类
通过继承 Thread
类并重写其 run()
方法来创建线程。
代码示例:继承 Thread 类创建线程
public class MyThread extends Thread {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " is running.");}public static void main(String[] args) {MyThread thread1 = new MyThread();MyThread thread2 = new MyThread();thread1.start();thread2.start();}
}
-
代码解析:
-
MyThread
类继承Thread
类,并重写run()
方法。 -
调用
start()
方法启动线程,run()
方法中的代码将在新线程中执行。
-
2.2 实现 Runnable 接口
通过实现 Runnable
接口并将其实例传递给 Thread
对象来创建线程。
代码示例:实现 Runnable 接口创建线程
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println(Thread.currentThread().getName() + " is running.");}public static void main(String[] args) {Thread thread1 = new Thread(new MyRunnable());Thread thread2 = new Thread(new MyRunnable());thread1.start();thread2.start();}
}
-
代码解析:
-
MyRunnable
类实现了Runnable
接口,并重写了run()
方法。 -
使用
new Thread(new MyRunnable())
创建Thread
对象,并调用start()
启动线程。
-
2.3 实现 Callable 接口
Callable
接口与 Runnable
类似,但可以返回一个结果或抛出异常。
代码示例:实现 Callable 接口创建线程
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;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName() + " is calculating.");return 42;}public static void main(String[] args) {ExecutorService executor = Executors.newSingleThreadExecutor();Future<Integer> future = executor.submit(new MyCallable());try {Integer result = future.get();System.out.println("Result: " + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}executor.shutdown();}
}
-
代码解析:
-
MyCallable
类实现了Callable
接口,并重写了call()
方法,返回一个整数。 -
使用
ExecutorService
提交Callable
任务,并通过Future
获取执行结果。
-
3. 线程的生命周期
线程在 Java 中的生命周期包括以下几种状态:
-
NEW:线程被创建,但尚未启动。
-
RUNNABLE:线程可以运行,正在等待 CPU 调度。
-
BLOCKED:线程被阻塞,等待进入同步块/方法。
-
WAITING:线程等待另一个线程执行特定操作(如调用
notify()
或notifyAll()
)。 -
TIMED_WAITING:线程等待指定的时间。
-
TERMINATED:线程已完成执行。
4. 线程同步
在多线程环境中,当多个线程访问共享资源时,需要进行同步以避免数据的不一致。Java 提供了多种方式来实现线程同步。
4.1 synchronized 关键字
synchronized
关键字可以用于方法或代码块,以保证同一时刻只有一个线程可以执行该代码。
代码示例:使用 synchronized 实现线程同步
public class SynchronizedExample {private int count = 0;// 同步方法public synchronized void increment() {count++;}public static void main(String[] args) throws InterruptedException {SynchronizedExample example = new SynchronizedExample();Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("Count: " + example.count);}
}
-
代码解析:
-
increment()
方法使用synchronized
关键字保证线程安全。 -
两个线程同时对
count
进行递增,使用同步确保最终结果正确。
-
4.2 ReentrantLock
ReentrantLock
是 Java 提供的一个锁实现,可以替代 synchronized
进行更灵活的同步控制。
代码示例:使用 ReentrantLock 实现线程同步
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {ReentrantLockExample example = new ReentrantLockExample();Thread thread1 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 1000; i++) {example.increment();}});thread1.start();thread2.start();thread1.join();thread2.join();System.out.println("Count: " + example.count);}
}
-
代码解析:
-
使用
ReentrantLock
来代替synchronized
,提供了更灵活的锁机制。 -
lock()
和unlock()
确保在执行临界区代码时只有一个线程进入。
-
5. 线程池
线程池是一种管理线程的机制,通过复用线程来减少创建和销毁线程的开销。Java 提供了 ExecutorService
接口来管理线程池。
5.1 使用 ExecutorService 创建线程池
代码示例:使用线程池执行任务
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(3);for (int i = 0; i < 5; i++) {int taskId = i;executor.execute(() -> {System.out.println("Task " + taskId + " is running by " + Thread.currentThread().getName());});}executor.shutdown();}
}
-
代码解析:
-
使用
Executors.newFixedThreadPool(3)
创建了一个包含 3 个线程的线程池。 -
execute()
方法将任务提交给线程池执行。
-
6. 并发工具类
Java 提供了一些实用的并发工具类来简化并发编程,如 CountDownLatch
、CyclicBarrier
、Semaphore
、ConcurrentHashMap
等。
6.1 CountDownLatch
CountDownLatch
允许一个或多个线程等待,直到其他线程完成一组操作。
代码示例:使用 CountDownLatch
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int threadCount = 3;CountDownLatch latch = new CountDownLatch(threadCount);for (int i = 0; i < threadCount; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + " finished work.");latch.countDown(); // 计数器减一}).start();}latch.await(); // 等待所有线程完成System.out.println("All threads have finished.");}
}
-
代码解析:
-
CountDownLatch
初始化时设置计数器为线程数,每个线程完成工作后调用countDown()
。 -
主线程调用
await()
,等待计数器归零后继续执行。
-
7. 线程创建方式对比
特性 | 继承 Thread 类 | 实现 Runnable 接口 | 实现 Callable 接口 |
---|---|---|---|
是否返回结果 | 否 | 否 | 是,可以返回结果并抛出异常 |
可扩展性 | 受限(只能单继承) | 高(可以实现多个接口) | 高(可以实现多个接口) |
复杂度 | 简单 | 较简单 | 较复杂 |
8. 结论
Java 多线程与并发编程为开发者提供了强大的工具,使得应用程序能够高效地执行并行任务。本篇文章详细介绍了线程的创建方式、线程的生命周期、线程同步、线程池以及并发工具类的使用,结合丰富的代码示例来帮助理解这些概念。掌握这些知识可以使开发者更加熟练地编写多线程程序,从而提高应用程序的性能和响应能力。在后续的文章中,我们将进一步探讨 Java 内存管理与垃圾回收机制,以帮助您更深入地理解 Java 的底层运行机制。
希望本篇文章能够帮助你更好地理解 Java 中的多线程与并发编程。如果有任何疑问或想深入了解某些概念,欢迎在评论区留言!