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 多线程与并发编程的基础知识和常用技术。
在实际开发中,需要根据具体的场景选择合适的并发模型和工具,避免出现死锁、竞态条件等并发问题,从而实现高效、安全的多线程编程。