Java 多线程与并发编程是 Java 工程师必须掌握的重要技能之一。随着计算机硬件性能的不断提升,多核 CPU 成为主流,开发高并发应用已成为越来越重要的需求。本文将介绍 Java 多线程与并发编程的基本概念和技术,包括线程、锁、同步机制、线程池、并发集合和并发编程的实践案例。
线程
线程是进程的一部分,是程序执行的最小单位。Java 中的线程是通过 Thread 类实现的。在 Java 中,线程有五个状态:新建状态、就绪状态、运行状态、阻塞状态和终止状态。通常使用 start 方法启动一个新线程,使用 join 方法等待线程执行完毕,使用 sleep 方法暂停线程的执行。以下是一个简单的线程实例:
public class MyThread extends Thread { @Override public void run() { System.out.println("Hello from thread " + Thread.currentThread().getName()); } public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); thread1.start(); thread2.start(); } }
锁和同步机制
-
在多线程编程中,需要使用锁和同步机制来保证线程安全。
-
Java 中提供了多种锁和同步机制,包括 synchronized 关键字、ReentrantLock 类、CountDownLatch 类、Semaphore 类等。
-
synchronized 关键字是 Java 中最常用的同步机制,可以用来保护临界区资源的访问,防止多个线程同时访问造成数据不一致的问题。以下是一个使用 synchronized 实现线程安全的计数器的例子:
public class Counter { private int count; public synchronized void increment() { count++; } public synchronized int getCount() { return count; } }
ReentrantLock 类是一个可重入锁,它提供了与 synchronized 关键字相似的同步功能,但更加灵活和可控。
以下是一个使用 ReentrantLock 实现线程安全的计数器的例子:
public class Counter { private int count; private ReentrantLock lock = new ReentrantLock(); public void increment() { lock.lock(); try { count++; } finally { lock.unlock(); } } public int getCount() { lock.lock(); try { return count; } finally { lock.unlock(); } } }
线程池
线程池是多线程编程中的一种常用技术,它可以管理和复用线程,提高程序的性能和可维护性。
Java 中提供了 Executor 框架和 ThreadPoolExecutor 类来实现线程池。以下是一个使用 ThreadPoolExecutor 实现的线程池例子:
public public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); for (int i = 0; i < 10; i++) { executor.execute(new WorkerThread("Thread " + i)); } executor.shutdown(); while (!executor.isTerminated()) { // 等待线程池中的任务执行完毕 } System.out.println("All threads have been terminated."); } } class WorkerThread implements Runnable { private String name; public WorkerThread(String name) { this.name = name; } @Override public void run() { System.out.println("Start executing thread " + name); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Finish executing thread " + name); } }
并发集合
Java 提供了一些并发集合类,例如 ConcurrentHashMap、ConcurrentLinkedQueue、CopyOnWriteArrayList 等,用于在多线程环境下安全地读写数据。这些集合类采用了一些特殊的技术,例如分段锁、CAS(Compare And Swap)等,来保证线程安全。
以下是一个使用 ConcurrentHashMap 实现的线程安全的计数器例子:
public class ConcurrentCounter { private ConcurrentHashMap<String, Integer> counter = new ConcurrentHashMap<>(); public void increment(String key) { counter.compute(key, (k, v) -> v == null ? 1 : v + 1); } public int getCount(String key) { return counter.get(key); } }
并发编程实践
下面介绍一个简单的并发编程实践:多线程下载器。该下载器使用多个线程同时下载一个文件,从而加快下载速度。
public class MultiThreadDownloader { private URL url; private int threadCount; public MultiThreadDownloader(String urlString, int threadCount) throws MalformedURLException { this.url = new URL(urlString); this.threadCount = threadCount; } public void download() throws IOException, InterruptedException { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); int fileSize = connection.getContentLength(); connection.disconnect(); int blockSize = fileSize / threadCount + 1; CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { int start = i * blockSize; int end = Math.min((i + 1) * blockSize - 1, fileSize - 1); new DownloadThread(url, start, end, latch).start(); } latch.await(); System.out.println("Download completed."); } public static void main(String[] args) throws IOException, InterruptedException { MultiThreadDownloader downloader = new MultiThreadDownloader("https://www.example.com/file.txt", 4); downloader.download(); } } class DownloadThread extends Thread { private URL url; private int start; private int end; private CountDownLatch latch; public DownloadThread(URL url, int start, int end, CountDownLatch latch) { this.url = url; this.start = start; this.end = end; this.latch = latch; } @Override public void run() { try { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("Range", "bytes=" + start + "-" + end); InputStream in = connection.getInputStream(); FileOutputStream out = new FileOutputStream("file.txt", true); byte[] buffer = new byte[4096]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } in.close(); out.close(); connection.disconnect(); System.out.println("Downloaded bytes " + start + "-" + end + "."); } catch (IOException e) { e.printStackTrace(); } finally { latch.countDown(); } } }
-
在主函数中,我们创建了一个 MultiThreadDownloader 对象,指定了下载文件的 URL 和线程数。
-
download 方法中,首先发送一个 HEAD 请求获取文件大小,然后根据线程数计算出每个线程下载的数据块大小。
-
接下来,使用 CountDownLatch 来控制所有线程的执行,创建 threadCount 个 DownloadThread 对象,并启动它们。
-
每个 DownloadThread 对象都指定了下载数据的起始位置和终止位置,以及一个 CountDownLatch 对象。
-
DownloadThread 的 run 方法中,首先发送一个 GET 请求,指定下载数据的范围,然后将数据写入本地文件,并计数器减一。最后在主函数中等待所有线程执行完毕。
总结
本文简单介绍了 Java 多线程与并发编程的基础知识和常用技术。
在实际开发中,需要根据具体的场景选择合适的并发模型和工具,避免出现死锁、竞态条件等并发问题,从而实现高效、安全的多线程编程。