Java中的并发与多线程:从理论到实践
在现代软件世界中,应用程序的性能直接取决于有效利用处理器资源的能力。Java中的并发与多线程是关键概念,它们允许同时执行多个任务、加速数据处理并创建响应迅速的用户界面。在本文中,我们将分析Java中多线程的基本机制,学习如何创建和管理线程,并探讨避免常见错误的最佳实践。
什么是线程以及如何在Java中创建它?
线程(thread)是一个轻量级进程,在单个应用程序内执行。每个线程都有自己的调用栈,并且可以独立于其他线程工作。在Java中,有两种主要的创建线程的方法。
方法1:继承Thread类
public class MyThread extends Thread { @Override public void run() { System.out.println("线程 " + getName() + " 已启动!"); }
public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // 启动线程 }}方法2:实现Runnable接口
public class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程通过Runnable执行"); }
public static void main(String[] args) { Thread thread = new Thread(new MyRunnable()); thread.start(); }}推荐使用第二种方法,因为它不需要继承,并且允许同时实现多个接口。
线程同步:避免竞争条件
当多个线程同时访问共享数据时,会出现竞争条件(race condition)。为了避免这种情况,Java提供了同步机制。
synchronized关键字
阻塞其他线程对方法或代码块的访问,直到当前线程执行完毕:
public class Counter { private int count = 0;
public synchronized void increment() { count++; }
public synchronized int getCount() { return count; }}使用Lock进行锁定
一种比synchronized更灵活的机制。允许设置超时并尝试获取锁而不阻塞:
import java.util.concurrent.locks.ReentrantLock;
public class SafeCounter { private final ReentrantLock lock = new ReentrantLock(); private int count = 0;
public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); // 始终释放锁 } }}线程池与ExecutorService
为每个任务创建新线程是一项昂贵的操作。为了有效管理线程,请使用java.util.concurrent包中的ExecutorService。
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
public class ThreadPoolExample { public static void main(String[] args) { // 创建一个包含4个线程的线程池 ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) { final int taskId = i; executor.submit(() -> { System.out.println("任务 " + taskId + " 正在 " + Thread.currentThread().getName() + " 中执行"); }); }
executor.shutdown(); // 在所有任务执行完毕后关闭线程池 }}主要的线程池类型:
newFixedThreadPool(n)— 固定数量的线程