JUC应用场景
1. 网页服务器处理并发请求
当一个网页服务器需要处理大量并发请求时,可以使用多线程来提高处理效率。
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class WebServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(8080);while (true) {Socket clientSocket = serverSocket.accept();new Thread(new ClientHandler(clientSocket)).start();}} } class ClientHandler implements Runnable {private Socket clientSocket; public ClientHandler(Socket socket) {this.clientSocket = socket;} @Overridepublic void run() {try {// 处理客户端请求clientSocket.getOutputStream().write("HTTP/1.1 200 OK\r\n\r\nHello, World!".getBytes());clientSocket.close();} catch (IOException e) {e.printStackTrace();}} }
2. 文件下载器
多线程下载可以加快大文件的下载速度。
import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; public class MultiThreadedDownloader {public static void main(String[] args) throws Exception {String fileURL = "http://example.com/file.zip";int numThreads = 4;new MultiThreadedDownloader().downloadFile(fileURL, numThreads);} public void downloadFile(String fileURL, int numThreads) throws Exception {URL url = new URL(fileURL);HttpURLConnection conn = (HttpURLConnection) url.openConnection();int fileSize = conn.getContentLength();conn.disconnect(); int partSize = fileSize / numThreads;RandomAccessFile file = new RandomAccessFile("downloaded_file.zip", "rw"); for (int i = 0; i < numThreads; i++) {int start = i * partSize;int end = (i == numThreads - 1) ? fileSize : (start + partSize - 1);new Thread(new DownloadThread(fileURL, start, end, file)).start();}} } class DownloadThread implements Runnable {private String fileURL;private int start;private int end;private RandomAccessFile file; public DownloadThread(String fileURL, int start, int end, RandomAccessFile file) {this.fileURL = fileURL;this.start = start;this.end = end;this.file = file;} @Overridepublic void run() {try {HttpURLConnection conn = (HttpURLConnection) new URL(fileURL).openConnection();String range = "bytes=" + start + "-" + end;conn.setRequestProperty("Range", range);conn.connect();byte[] buffer = new byte[1024];int bytesRead;try (RandomAccessFile partFile = new RandomAccessFile(file.getFD(), "rw")) {partFile.seek(start);while ((bytesRead = conn.getInputStream().read(buffer, 0, 1024)) != -1) {partFile.write(buffer, 0, bytesRead);}}conn.disconnect();} catch (Exception e) {e.printStackTrace();}} }
3. 线程池处理任务
使用线程池管理多线程任务,避免频繁创建和销毁线程带来的开销。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ThreadPoolExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(4); for (int i = 0; i < 10; i++) {executor.submit(new Task(i));} executor.shutdown();try {if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();}} catch (InterruptedException e) {executor.shutdownNow();}} } class Task implements Runnable {private int taskId; public Task(int id) {this.taskId = id;} @Overridepublic void run() {System.out.println("Executing Task " + taskId + " by " + Thread.currentThread().getName());try {Thread.sleep(2000); // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Completed Task " + taskId);} }
4. 生产者-消费者模型
在实际业务中,生产者-消费者模型常用于任务调度、消息队列等场景。
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class ProducerConsumerExample {public static void main(String[] args) {BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); Thread producerThread = new Thread(new Producer(queue));Thread consumerThread = new Thread(new Consumer(queue)); producerThread.start();consumerThread.start();} } class Producer implements Runnable {private BlockingQueue<Integer> queue; public Producer(BlockingQueue<Integer> queue) {this.queue = queue;} @Overridepublic void run() {for (int i = 0; i < 20; i++) {try {queue.put(i);System.out.println("Produced: " + i);Thread.sleep(100); // 模拟生产延迟} catch (InterruptedException e) {e.printStackTrace();}}} } class Consumer implements Runnable {private BlockingQueue<Integer> queue; public Consumer(BlockingQueue<Integer> queue) {this.queue = queue;} @Overridepublic void run() {while (true) {try {int value = queue.take();System.out.println("Consumed: " + value);Thread.sleep(150); // 模拟消费延迟} catch (InterruptedException e) {e.printStackTrace();}}} }
5. 并行计算
在数据处理和计算任务中,多线程可以显著提高性能。例如,计算一个大数组的总和。
import java.util.concurrent.RecursiveTask; import java.util.concurrent.ForkJoinPool; public class ParallelSum {private static final int THRESHOLD = 10_000; public static void main(String[] args) {int[] array = new int[100_000];for (int i = 0; i < array.length; i++) {array[i] = i + 1;} ForkJoinPool forkJoinPool = new ForkJoinPool();Long sum = forkJoinPool.invoke(new SumTask(array, 0, array.length));System.out.println("Sum: " + sum);} } class SumTask extends RecursiveTask<Long> {private int[] array;private int start;private int end; public SumTask(int[] array, int start, int end) {this.array = array;this.start = start;this.end = end;} @Overrideprotected Long compute() {if (end - start <= ParallelSum.THRESHOLD) {long sum = 0;for (int i = start; i < end; i++) {sum += array[i];}return sum;} else {int mid = (start + end) / 2;SumTask leftTask = new SumTask(array, start, mid);SumTask rightTask = new SumTask(array, mid, end); leftTask.fork();long rightResult = rightTask.compute();long leftResult = leftTask.join(); return leftResult + rightResult;}} }
当然可以,下面是一些更多实际业务中应用 Java 多线程技术的场景示例。
6. 定时任务调度
在某些业务场景中,需要定时执行任务,比如定时备份数据库、清理日志等,可以使用多线程来实现定时任务调度。
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledTaskExample {public static void main(String[] args) {ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); Runnable task = () -> System.out.println("Executing Task at " + System.currentTimeMillis());// 5秒后执行任务,每10秒执行一次scheduler.scheduleAtFixedRate(task, 5, 10, TimeUnit.SECONDS);} }
7. 并行数据处理
在大数据处理或数据流处理场景中,使用多线程可以显著提高数据处理速度。
import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ParallelDataProcessing {public static void main(String[] args) {List<String> data = Arrays.asList("data1", "data2", "data3", "data4", "data5");ExecutorService executor = Executors.newFixedThreadPool(3); for (String item : data) {executor.submit(() -> processItem(item));} executor.shutdown();} private static void processItem(String item) {System.out.println("Processing " + item + " by " + Thread.currentThread().getName());try {Thread.sleep(1000); // 模拟处理时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Completed " + item);} }
8. 异步任务处理
在一些业务场景中,需要执行异步任务以提高系统的响应速度,例如异步处理用户请求等。
import java.util.concurrent.CompletableFuture; public class AsyncTaskExample {public static void main(String[] args) {System.out.println("Main thread: " + Thread.currentThread().getName()); CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {System.out.println("Async task thread: " + Thread.currentThread().getName());try {Thread.sleep(2000); // 模拟异步任务处理} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Async task completed");}); future.thenRun(() -> System.out.println("Continuation task thread: " + Thread.currentThread().getName()));} }
9. 多线程计算任务
在一些计算密集型任务中,比如矩阵运算、多项式计算等,可以使用多线程提高计算效率。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class MultiThreadedMatrixMultiplication {private static final int MATRIX_SIZE = 3;public static void main(String[] args) {int[][] matrixA = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };int[][] matrixB = { {9, 8, 7}, {6, 5, 4}, {3, 2, 1} };int[][] result = new int[MATRIX_SIZE][MATRIX_SIZE];ExecutorService executor = Executors.newFixedThreadPool(MATRIX_SIZE);for (int i = 0; i < MATRIX_SIZE; i++) {for (int j = 0; j < MATRIX_SIZE; j++) {executor.submit(new MatrixMultiplicationTask(matrixA, matrixB, result, i, j));}}executor.shutdown();while (!executor.isTerminated()) {}for (int[] row : result) {for (int value : row) {System.out.print(value + " ");}System.out.println();}} }class MatrixMultiplicationTask implements Runnable {private int[][] matrixA;private int[][] matrixB;private int[][] result;private int row;private int col;public MatrixMultiplicationTask(int[][] matrixA, int[][] matrixB, int[][] result, int row, int col) {this.matrixA = matrixA;this.matrixB = matrixB;this.result = result;this.row = row;this.col = col;}@Overridepublic void run() {for (int k = 0; k < matrixA.length; k++) {result[row][col] += matrixA[row][k] * matrixB[k][col];}} }
10. 实时数据分析
在金融、物联网等领域,需要实时分析数据,可以使用多线程来提高处理速度。
import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue;public class RealTimeDataAnalysis {public static void main(String[] args) {BlockingQueue<String> dataQueue = new LinkedBlockingQueue<>();Thread producerThread = new Thread(new DataProducer(dataQueue));Thread consumerThread = new Thread(new DataConsumer(dataQueue));producerThread.start();consumerThread.start();} }class DataProducer implements Runnable {private BlockingQueue<String> queue;public DataProducer(BlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {String data = "Data-" + i;queue.put(data);System.out.println("Produced: " + data);Thread.sleep(500); // 模拟数据生成延迟} catch (InterruptedException e) {e.printStackTrace();}}} }class DataConsumer implements Runnable {private BlockingQueue<String> queue;public DataConsumer(BlockingQueue<String> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {String data = queue.take();System.out.println("Consumed: " + data);Thread.sleep(1000); // 模拟数据处理时间} catch (InterruptedException e) {e.printStackTrace();}}} }
JUC核心知识点
Java 并发工具包(Java Concurrency Utilities,简称 JUC)是 Java 提供的一套强大的并发工具,用于简化多线程编程。JUC 位于 java.util.concurrent
包中,包含了多种并发编程的核心工具和类。下面是 JUC 的一些核心知识点以及如何使用这些工具。
1. 基本线程池和任务执行
1.1 Executor 框架
Executor
框架提供了一种将任务提交与任务执行分离的方法。核心接口是 Executor
和 ExecutorService
。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ExecutorExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(3);for (int i = 0; i < 5; i++) {executor.submit(new Task());}executor.shutdown();} }class Task implements Runnable {@Overridepublic void run() {System.out.println("Executing Task by " + Thread.currentThread().getName());try {Thread.sleep(2000); // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Task Completed by " + Thread.currentThread().getName());} }
2. 同步容器类
2.1 BlockingQueue
BlockingQueue
是一个线程安全的队列,常用于生产者-消费者模型。
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue;public class BlockingQueueExample {public static void main(String[] args) {BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);Thread producerThread = new Thread(new Producer(queue));Thread consumerThread = new Thread(new Consumer(queue));producerThread.start();consumerThread.start();} }class Producer implements Runnable {private BlockingQueue<Integer> queue;public Producer(BlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {queue.put(i);System.out.println("Produced: " + i);Thread.sleep(100); // 模拟生产延迟} catch (InterruptedException e) {e.printStackTrace();}}} }class Consumer implements Runnable {private BlockingQueue<Integer> queue;public Consumer(BlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {int value = queue.take();System.out.println("Consumed: " + value);Thread.sleep(150); // 模拟消费延迟} catch (InterruptedException e) {e.printStackTrace();}}} }
3. 并发工具类
3.1 CountDownLatch
CountDownLatch
用于使一个或多个线程等待其他线程完成一组操作。
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int count = 3;CountDownLatch latch = new CountDownLatch(count);for (int i = 0; i < count; i++) {new Thread(new Worker(latch)).start();}latch.await(); // 等待所有工作线程完成System.out.println("All workers are done.");} }class Worker implements Runnable {private CountDownLatch latch;public Worker(CountDownLatch latch) {this.latch = latch;}@Overridepublic void run() {System.out.println("Worker " + Thread.currentThread().getName() + " is working...");try {Thread.sleep(2000); // 模拟工作} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Worker " + Thread.currentThread().getName() + " done.");latch.countDown(); // 完成工作,计数减一} }
3.2 CyclicBarrier
CyclicBarrier
用于使一组线程互相等待,直到到达某个公共屏障点。
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;public class CyclicBarrierExample {public static void main(String[] args) {int count = 3;CyclicBarrier barrier = new CyclicBarrier(count, () -> System.out.println("All parties have arrived."));for (int i = 0; i < count; i++) {new Thread(new Worker(barrier)).start();}} }class Worker implements Runnable {private CyclicBarrier barrier;public Worker(CyclicBarrier barrier) {this.barrier = barrier;}@Overridepublic void run() {System.out.println("Worker " + Thread.currentThread().getName() + " is working...");try {Thread.sleep(2000); // 模拟工作System.out.println("Worker " + Thread.currentThread().getName() + " reached barrier.");barrier.await(); // 等待其他线程} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}} }
4. 并发集合类
4.1 ConcurrentHashMap
ConcurrentHashMap
是一个线程安全的哈希表,支持高并发性。
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();ExecutorService executor = Executors.newFixedThreadPool(4);for (int i = 0; i < 10; i++) {int index = i;executor.submit(() -> {map.put("key" + index, index);System.out.println("Added: key" + index + " by " + Thread.currentThread().getName());});}executor.shutdown();while (!executor.isTerminated()) {}map.forEach((key, value) -> System.out.println(key + ": " + value));} }
5. 原子类
5.1 AtomicInteger
AtomicInteger
提供了一种线程安全的方式来更新整数值。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerExample {public static void main(String[] args) {AtomicInteger counter = new AtomicInteger();ExecutorService executor = Executors.newFixedThreadPool(3);for (int i = 0; i < 10; i++) {executor.submit(() -> {int value = counter.incrementAndGet();System.out.println("Counter: " + value);});}executor.shutdown();} }
6. Future 和 Callable
Future
和 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 FutureCallableExample {public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(3);Future<Integer> future = executor.submit(new Task());try {Integer result = future.get(); // 获取异步任务结果System.out.println("Result: " + result);} catch (InterruptedException | ExecutionException e) {e.printStackTrace();}executor.shutdown();} }class Task implements Callable<Integer> {@Overridepublic Integer call() throws Exception {Thread.sleep(2000); // 模拟计算return 42;} }
7. ReentrantLock
ReentrantLock
是一种比 synchronized
更灵活的锁机制。
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final Lock lock = new ReentrantLock();public static void main(String[] args) {ReentrantLockExample example = new ReentrantLockExample();Runnable task = example::performTask;Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();}public void performTask() {lock.lock();try {System.out.println("Locked by " + Thread.currentThread().getName());Thread.sleep(2000); // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println("Unlocked by " + Thread.currentThread().getName());lock.unlock();}} }
8. 读写锁(ReadWriteLock)
ReadWriteLock
允许多个读线程同时访问,但写线程独占访问。
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private int sharedData = 0;public static void main(String[] args) {ReadWriteLockExample example = new ReadWriteLockExample();Runnable readTask = () -> {for (int i = 0; i < 5; i++) {example.readData();try {Thread.sleep(100); // 模拟读取操作时间} catch (InterruptedException e) {e.printStackTrace();}}};Runnable writeTask = () -> {for (int i = 0; i < 5; i++) {example.writeData(i);try {Thread.sleep(100); // 模拟写入操作时间} catch (InterruptedException e) {e.printStackTrace();}}};Thread t1 = new Thread(readTask);Thread t2 = new Thread(readTask);Thread t3 = new Thread(writeTask);t1.start();t2.start();t3.start();}public void readData() {readWriteLock.readLock().lock();try {System.out.println(Thread.currentThread().getName() + " Reading: " + sharedData);} finally {readWriteLock.readLock().unlock();}}public void writeData(int data) {readWriteLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName() + " Writing: " + data);sharedData = data;} finally {readWriteLock.writeLock().unlock();}} }
9. 线程协作
9.1 Semaphore
Semaphore
用于控制同时访问特定资源的线程数量。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore;public class SemaphoreExample {private static final int MAX_PERMITS = 3;private final Semaphore semaphore = new Semaphore(MAX_PERMITS);public static void main(String[] args) {SemaphoreExample example = new SemaphoreExample();ExecutorService executor = Executors.newFixedThreadPool(10);for (int i = 0; i < 10; i++) {executor.submit(example::performTask);}executor.shutdown();}public void performTask() {try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + " Acquired permit.");Thread.sleep(2000); // 模拟任务执行} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(Thread.currentThread().getName() + " Released permit.");semaphore.release();}} }
9.2 Exchanger
Exchanger
用于两个线程之间交换数据。
import java.util.concurrent.Exchanger;public class ExchangerExample {public static void main(String[] args) {Exchanger<String> exchanger = new Exchanger<>();new Thread(new DataProducer(exchanger)).start();new Thread(new DataConsumer(exchanger)).start();} }class DataProducer implements Runnable {private Exchanger<String> exchanger;public DataProducer(Exchanger<String> exchanger) {this.exchanger = exchanger;}@Overridepublic void run() {try {String data = "Producer Data";System.out.println("Produced: " + data);String receivedData = exchanger.exchange(data);System.out.println("Received by Producer: " + receivedData);} catch (InterruptedException e) {e.printStackTrace();}} }class DataConsumer implements Runnable {private Exchanger<String> exchanger;public DataConsumer(Exchanger<String> exchanger) {this.exchanger = exchanger;}@Overridepublic void run() {try {String data = "Consumer Data";System.out.println("Produced: " + data);String receivedData = exchanger.exchange(data);System.out.println("Received by Consumer: " + receivedData);} catch (InterruptedException e) {e.printStackTrace();}} }
10. Fork/Join 框架
Fork/Join 框架用于将大任务拆分成多个小任务并行执行。适用于计算密集型任务。
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask;public class ForkJoinExample {public static void main(String[] args) {ForkJoinPool forkJoinPool = new ForkJoinPool();int[] array = new int[100];for (int i = 0; i < array.length; i++) {array[i] = i + 1;}SumTask task = new SumTask(array, 0, array.length);int result = forkJoinPool.invoke(task);System.out.println("Sum: " + result);} }class SumTask extends RecursiveTask<Integer> {private static final int THRESHOLD = 10;private int[] array;private int start;private int end;public SumTask(int[] array, int start, int end) {this.array = array;this.start = start;this.end = end;}@Overrideprotected Integer compute() {if (end - start <= THRESHOLD) {int sum = 0;for (int i = start; i < end; i++) {sum += array[i];}return sum;} else {int mid = (start + end) / 2;SumTask leftTask = new SumTask(array, start, mid);SumTask rightTask = new SumTask(array, mid, end);leftTask.fork();rightTask.fork();return leftTask.join() + rightTask.join();}} }
总结
JUC 提供了一系列强大的工具和类,帮助开发者更容易地实现高效的并发程序。在实际开发中,选择合适的并发工具和类,并根据业务需求进行优化,可以显著提升应用程序的性能和响应速度。希望以上示例能够帮助你更好地理解和应用 JUC 的核心知识点。
juc面试
在中国互联网公司中,关于 JUC(Java 并发工具包)的面试题往往涉及到多线程编程的基础知识、线程安全问题以及 JUC 提供的各种工具和类的实际应用。以下是一些常见的面试题示例:
1. 基础概念
-
什么是线程安全?如何保证线程安全?
-
线程安全是指多个线程访问共享资源时,不会导致数据不一致或程序异常。可以通过同步机制(如
synchronized
关键字、Lock
接口)或使用线程安全的类(如ConcurrentHashMap
)来保证线程安全。
-
-
什么是死锁?如何避免死锁?
-
死锁是指两个或多个线程相互等待对方释放资源,导致程序无法继续执行。避免死锁的方法包括资源排序法、定时锁尝试以及使用高层次的锁机制(如
ReentrantLock
)。
-
2. JUC 工具类
-
解释
CountDownLatch
的原理及使用场景。-
CountDownLatch
是一个同步辅助类,用于使一个或多个线程等待其他线程完成一组操作。常见使用场景包括并行计算中的阶段同步。
import java.util.concurrent.CountDownLatch;public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int count = 3;CountDownLatch latch = new CountDownLatch(count);for (int i = 0; i < count; i++) {new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}latch.countDown();}).start();}latch.await();System.out.println("All tasks completed.");} }
-
-
解释
CyclicBarrier
的原理及使用场景。-
CyclicBarrier
是一个同步辅助类,使一组线程相互等待,直到到达某个公共屏障点。常见使用场景包括分段任务的汇总,如多线程分段计算结果的合并。
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;public class CyclicBarrierExample {public static void main(String[] args) {int count = 3;CyclicBarrier barrier = new CyclicBarrier(count, () -> System.out.println("All parties have arrived."));for (int i = 0; i < count; i++) {new Thread(() -> {try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName() + " reached barrier.");barrier.await();} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}).start();}} }
-
-
Semaphore
的使用场景及如何实现限流。-
Semaphore
用于控制同时访问特定资源的线程数量。常见使用场景包括限流和资源池管理。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore;public class SemaphoreExample {private static final int MAX_PERMITS = 3;private final Semaphore semaphore = new Semaphore(MAX_PERMITS);public static void main(String[] args) {SemaphoreExample example = new SemaphoreExample();ExecutorService executor = Executors.newFixedThreadPool(10);for (int i = 0; i < 10; i++) {executor.submit(example::performTask);}executor.shutdown();}public void performTask() {try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + " Acquired permit.");Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(Thread.currentThread().getName() + " Released permit.");semaphore.release();}} }
-
3. 线程池
-
线程池的工作原理是什么?常见的线程池有哪些?
-
线程池通过重用已创建的线程来减少线程创建和销毁的开销。常见的线程池包括
FixedThreadPool
、CachedThreadPool
、ScheduledThreadPool
和SingleThreadExecutor
。
-
-
如何自定义线程池?
-
可以使用
ThreadPoolExecutor
类来自定义线程池,指定核心线程数、最大线程数、空闲线程存活时间、任务队列以及线程工厂和拒绝策略。
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit;public class CustomThreadPool {public static void main(String[] args) {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));for (int i = 0; i < 10; i++) {executor.execute(() -> {System.out.println(Thread.currentThread().getName() + " is working.");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}executor.shutdown();} }
-
4. 原子类和锁
-
什么是 CAS?CAS 是如何实现线程安全的?
-
CAS(Compare-And-Swap)是一种原子操作,用于实现无锁并发。它通过比较内存中的值和预期值,如果相等则更新为新值,否则继续重试。
-
-
ReentrantLock
和synchronized
的区别?-
ReentrantLock
提供了比synchronized
更加灵活的锁机制,支持公平锁、非公平锁以及在同一个线程中可以多次获取锁。synchronized
是 Java 内置的同步机制,使用简单但功能有限。
-
5. 并发编程中的经典问题
-
生产者-消费者问题如何解决?
-
可以使用
BlockingQueue
、Semaphore
或者自定义锁和条件变量来解决生产者-消费者问题。
-
-
哲学家就餐问题如何解决?
-
可以使用
ReentrantLock
或者Semaphore
来解决哲学家就餐问题,避免死锁和饥饿问题。
-
继续介绍一些关于 JUC 的常见面试题和相关概念,帮助你更好地准备面试。
6. 高级锁机制
6.1 ReadWriteLock
的使用场景和实现原理
-
ReadWriteLock
允许多个读线程同时访问,但写线程独占访问,适用于读多写少的场景。 -
ReentrantReadWriteLock
是ReadWriteLock
的一个实现,提供了readLock
和writeLock
。
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();private int sharedData = 0;public void readData() {readWriteLock.readLock().lock();try {System.out.println(Thread.currentThread().getName() + " Reading: " + sharedData);} finally {readWriteLock.readLock().unlock();}}public void writeData(int data) {readWriteLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName() + " Writing: " + data);sharedData = data;} finally {readWriteLock.writeLock().unlock();}}public static void main(String[] args) {ReadWriteLockExample example = new ReadWriteLockExample();Runnable readTask = example::readData;Runnable writeTask = () -> example.writeData(42);new Thread(readTask).start();new Thread(writeTask).start();} }
6.2 StampedLock
的使用场景和实现原理
-
StampedLock
是一种改进的读写锁,提供了更高的并发性能,尤其是在读多写少的场景下。 -
StampedLock
提供了乐观读锁,允许无锁读取,只有在检测到数据变化时才进行加锁。
import java.util.concurrent.locks.StampedLock;public class StampedLockExample {private final StampedLock stampedLock = new StampedLock();private int sharedData = 0;public void readData() {long stamp = stampedLock.tryOptimisticRead();int data = sharedData;if (!stampedLock.validate(stamp)) {stamp = stampedLock.readLock();try {data = sharedData;} finally {stampedLock.unlockRead(stamp);}}System.out.println(Thread.currentThread().getName() + " Reading: " + data);}public void writeData(int data) {long stamp = stampedLock.writeLock();try {sharedData = data;System.out.println(Thread.currentThread().getName() + " Writing: " + data);} finally {stampedLock.unlockWrite(stamp);}}public static void main(String[] args) {StampedLockExample example = new StampedLockExample();Runnable readTask = example::readData;Runnable writeTask = () -> example.writeData(42);new Thread(readTask).start();new Thread(writeTask).start();} }
7. 并发集合类
7.1 ConcurrentHashMap
的实现原理和使用
-
ConcurrentHashMap
是线程安全的哈希表,实现了高并发性能。 -
通过分段锁机制,每个桶有自己的锁,从而减少锁的竞争。
import java.util.concurrent.ConcurrentHashMap;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();map.put("key1", 1);map.put("key2", 2);map.forEach((key, value) -> System.out.println(key + ": " + value));new Thread(() -> map.put("key3", 3)).start();new Thread(() -> System.out.println(map.get("key3"))).start();} }
7.2 ConcurrentLinkedQueue
的实现原理和使用
-
ConcurrentLinkedQueue
是一个基于无锁算法的线程安全队列,适用于高并发场景。 -
使用 CAS 操作保证线程安全和高效性。
import java.util.concurrent.ConcurrentLinkedQueue;public class ConcurrentLinkedQueueExample {public static void main(String[] args) {ConcurrentLinkedQueue<Integer> queue = new ConcurrentLinkedQueue<>();queue.add(1);queue.add(2);System.out.println("Queue size: " + queue.size());new Thread(() -> queue.add(3)).start();new Thread(() -> System.out.println(queue.poll())).start();} }
8. 原子类
8.1 AtomicInteger
的使用场景和实现原理
-
AtomicInteger
提供了一种线程安全的方式来更新整数值,基于 CAS 实现。 -
常用于计数器和计量器等场景。
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerExample {public static void main(String[] args) {AtomicInteger counter = new AtomicInteger();Runnable task = () -> {for (int i = 0; i < 1000; i++) {counter.incrementAndGet();}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Counter: " + counter.get());} }
8.2 AtomicReference
的使用场景和实现原理
-
AtomicReference
提供了一种线程安全的方式来更新对象引用。 -
常用于需要原子更新复杂对象的场景。
import java.util.concurrent.atomic.AtomicReference;public class AtomicReferenceExample {public static void main(String[] args) {AtomicReference<String> reference = new AtomicReference<>("initial");Runnable task = () -> {for (int i = 0; i < 10; i++) {String oldValue = reference.get();String newValue = oldValue + " updated";reference.compareAndSet(oldValue, newValue);}};Thread t1 = new Thread(task);Thread t2 = new Thread(task);t1.start();t2.start();try {t1.join();t2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final value: " + reference.get());} }
9. 并发问题解决方案
9.1 生产者-消费者问题的解决方案
-
可以使用
BlockingQueue
实现生产者-消费者模式,简化线程间通信和同步。
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue;public class ProducerConsumerExample {public static void main(String[] args) {BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);Thread producer = new Thread(() -> {for (int i = 0; i < 20; i++) {try {queue.put(i);System.out.println("Produced: " + i);} catch (InterruptedException e) {e.printStackTrace();}}});Thread consumer = new Thread(() -> {while (true) {try {int value = queue.take();System.out.println("Consumed: " + value);} catch (InterruptedException e) {e.printStackTrace();}}});producer.start();consumer.start();} }
10. Fork/Join 框架
10.1 Fork/Join 框架的使用场景和实现原理
-
Fork/Join 框架用于将大任务拆分成多个小任务并行执行,适用于计算密集型任务。
-
通过
ForkJoinPool
和RecursiveTask
或RecursiveAction
实现。
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask;public class ForkJoinExample {public static void main(String[] args) {ForkJoinPool forkJoinPool = new ForkJoinPool();int[] array = new int[100];for (int i = 0; i < array.length; i++) {array[i] = i + 1;}SumTask task = new SumTask(array, 0, array.length);int result = forkJoinPool.invoke(task);System.out.println("Sum: " + result);} }class SumTask extends RecursiveTask<Integer> {private static final int THRESHOLD = 10;private int[] array;private int start;private int end;public SumTask(int[] array, int start, int end) {this.array = array;this.start = start;this.end = end;}@Overrideprotected Integer compute() {if (end - start <= THRESHOLD) {int sum = 0;for (int i = start; i < end; i++) {sum += array[i];}return sum;
线程池的数量与系统的 CPU 核心数
Java 线程池的数量设置与系统的 CPU 核心数有很大关系。合理设置线程池的大小可以充分利用系统资源,提高程序的并发性能和响应速度。以下是一些关于如何根据 CPU 核心数来设置线程池数量的原则和方法。
1. CPU 密集型任务
CPU 密集型任务:主要依赖于处理器进行大量计算,常见的任务包括复杂计算、图像处理、加密/解密等。
对于 CPU 密集型任务(即主要消耗 CPU 资源的任务),线程数的设置应接近于 CPU 核心数。这样可以避免过多的线程上下文切换,从而提高 CPU 使用效率。
建议线程数公式:
$$
\text{线程数} = \text{CPU 核心数} + 1
$$
其中,多出来的一个线程是为了应对偶尔的 I/O 操作或者其它非 CPU 密集型操作。
2. I/O 密集型任务
IO 密集型任务:主要依赖于输入输出操作,常见的任务包括文件读写、网络通信、数据库操作等。
对于 I/O 密集型任务(即主要消耗 I/O 资源如文件读写、网络通信的任务),由于 I/O 操作等待时间较长,CPU 会有大量空闲时间,因此可以设置更多的线程来充分利用这些空闲时间。
建议线程数公式:
$$
\text{线程数} = \text{CPU 核心数} \times (1 + \frac{\text{等待时间}}{\text{CPU 时间}})
$$
其中,等待时间是指任务在等待 I/O 操作完成的时间,CPU 时间是指任务实际消耗 CPU 资源的时间。
3. 混合型任务
对于同时包含 CPU 密集型和 I/O 密集型操作的混合型任务,需要根据具体情况进行调优。可以通过分析任务的 CPU 和 I/O 比例,参考上述两种类型任务的线程数计算方法,综合确定合适的线程池大小。
4. 实际案例
假设一个 Windows 系统的 CPU 核心数为 8:
-
CPU 密集型任务:建议线程池大小为 (8 + 1 = 9)。
-
I/O 密集型任务:假设等待时间是 CPU 时间的 10 倍,建议线程池大小为
$$
8 \times (1 + \frac{10}{1}) = 88
$$。
5. 使用 Runtime.getRuntime().availableProcessors()
在 Java 中,可以使用 Runtime.getRuntime().availableProcessors()
获取系统的 CPU 核心数,并根据上述原则动态设置线程池大小。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;public class ThreadPoolExample {public static void main(String[] args) {int cpuCores = Runtime.getRuntime().availableProcessors();// CPU 密集型任务线程池ExecutorService cpuThreadPool = Executors.newFixedThreadPool(cpuCores + 1);// I/O 密集型任务线程池int ioThreads = cpuCores * (1 + 10); // 假设等待时间是 CPU 时间的 10 倍ExecutorService ioThreadPool = Executors.newFixedThreadPool(ioThreads);// 提交任务示例for (int i = 0; i < 100; i++) {cpuThreadPool.execute(() -> {// CPU 密集型任务performCpuTask();});ioThreadPool.execute(() -> {// I/O 密集型任务performIoTask();});}cpuThreadPool.shutdown();ioThreadPool.shutdown();}private static void performCpuTask() {// 模拟 CPU 密集型任务for (int i = 0; i < 1000000; i++) {Math.sqrt(i);}}private static void performIoTask() {// 模拟 I/O 密集型任务try {Thread.sleep(100); // 模拟 I/O 等待} catch (InterruptedException e) {Thread.currentThread().interrupt();}} }
6. 监控与调整
实际应用中,线程池大小的设置可能需要根据运行情况进行调整和优化。可以使用监控工具(如 JVisualVM、JProfiler 等)来观察线程池的运行情况,并根据负载和性能表现进行调整。
7. 其它注意事项
-
线程池的类型:根据具体应用场景选择合适的线程池类型(如
FixedThreadPool
,CachedThreadPool
,ScheduledThreadPool
等)。 -
任务队列的类型:根据任务特点选择合适的任务队列(如
LinkedBlockingQueue
,ArrayBlockingQueue
等)。 -
资源限制:考虑系统的内存、I/O 带宽等资源限制,避免设置过大的线程池导致资源争用和性能下降。
通过合理设置线程池的大小,可以有效提高程序的并发性能和资源利用率。希望这些建议和示例代码能对你有所帮助。
Java对象内存布局和加锁机制
Java 对象内存布局
Java 对象在内存中的布局主要包括三个部分:
-
对象头(Object Header)
-
实例数据(Instance Data)
-
对齐填充(Padding)
1. 对象头(Object Header)
对象头由以下两部分组成:
-
Mark Word: 存储对象的运行时数据,如哈希码(hash code)、GC 分代年龄(GC generational age)、锁状态标志(lock state)、偏向锁(biased locking)标志等。
-
Class Pointer: 指向对象的类元数据(Class Metadata),即对象所属的类。
Mark Word
Mark Word 是一个多功能的字段,在不同状态下包含不同的信息。Mark Word 通常占用 32 位或 64 位空间,取决于使用的是 32 位还是 64 位的 JVM。
-
未锁定(Unlocked)状态:
-
32 位:对象哈希码、GC 分代年龄
-
64 位:对象哈希码、GC 分代年龄
-
-
偏向锁(Biased Locking)状态:
-
32 位:线程 ID、Epoch、偏向锁标志
-
64 位:线程 ID、Epoch、偏向锁标志
-
-
轻量级锁(Lightweight Locking)状态:
-
存储指向栈中锁记录的指针(lock record pointer)
-
-
重量级锁(Heavyweight Locking)状态:
-
存储指向重量级锁(monitor)的指针
-
-
GC 标记(GC Marking)状态:
-
GC 用于标记对象的标记信息
-
Class Pointer
Class Pointer 指向对象的类元数据,即对象所属类的定义。这有助于 JVM 在运行时确定对象的类型信息。
2. 实例数据(Instance Data)
实例数据存储的是对象的实际字段值,包括从父类继承下来的字段。实例数据的排列顺序取决于字段在类中的声明顺序以及 JVM 的实现。
3. 对齐填充(Padding)
对齐填充是为了保证对象的大小是 8 字节的倍数(某些 JVM 要求是 16 字节的倍数),以便于内存对齐和访问效率。
加锁的原理
Java 中的锁机制主要包括以下几种:
-
无锁(No Lock)
-
偏向锁(Biased Locking)
-
轻量级锁(Lightweight Locking)
-
重量级锁(Heavyweight Locking)
1. 无锁(No Lock)
当对象没有被任何线程锁定时,Mark Word 存储对象的哈希码和 GC 分代年龄。这是对象的初始状态。
2. 偏向锁(Biased Locking)
偏向锁的设计是为了优化只有一个线程访问同步块的情况。当一个线程首次获取偏向锁时,Mark Word 中记录该线程的 ID。如果同一个线程再次进入同步块,无需进行同步操作,可以大大减少开销。当另一个线程尝试获取该锁时,偏向锁会被撤销。
3. 轻量级锁(Lightweight Locking)
轻量级锁使用 CAS(Compare-And-Swap)操作来尝试获取锁。如果获取成功,Mark Word 会记录指向线程栈中锁记录的指针。如果获取失败,会膨胀为重量级锁。轻量级锁适用于锁竞争不激烈的场景。
4. 重量级锁(Heavyweight Locking)
重量级锁使用操作系统的互斥量(Mutex)实现,性能开销较大。当锁竞争激烈时,轻量级锁会膨胀为重量级锁,Mark Word 中记录指向重量级锁(monitor)的指针。
总结
Java 对象内存布局中的对象头和锁机制是 JVM 实现高效并发的关键部分。通过理解这些底层原理,可以更好地优化并发程序的性能和稳定性。
Java内存模型
Java 内存模型(Java Memory Model,JMM)是 Java 虚拟机(JVM)中定义的一组规范,旨在解决多线程程序中共享变量访问的可见性和指令重排序问题。JMM 确保了多线程环境下程序的正确性和一致性。
1. 基本概念
内存可见性
内存可见性是指一个线程对共享变量的修改,何时能被其他线程看到。在多线程程序中,每个线程都有自己的工作内存(工作缓存),用于存储共享变量的副本。JMM 确保了在特定的操作顺序下,线程对共享变量的修改能够被其他线程及时看到。
指令重排序
指令重排序是编译器和处理器为了优化程序性能,对指令顺序进行重新排列。在单线程环境中,重排序不会影响程序的正确性,但在多线程环境中,重排序可能导致不可预期的并发问题。JMM 通过 happens-before 规则来约束指令重排序。
2. JMM 的关键原则
happens-before 关系
happens-before 关系定义了操作之间的顺序性和可见性规则。它确保一个操作的结果对另一个操作是可见的。以下是 happens-before 规则的主要内容:
-
程序顺序规则: 在一个线程内,按照程序顺序,前面的操作 happens-before 于后面的操作。
-
监视器锁规则: 对一个锁的解锁 happens-before 于后续对这个锁的加锁。
-
volatile 变量规则: 对一个
volatile
变量的写操作 happens-before 于后续对这个volatile
变量的读操作。 -
线程启动规则: 一个线程的
Thread.start()
调用 happens-before 于这个线程内的所有操作。 -
线程终止规则: 一个线程的所有操作 happens-before 于对这个线程的
Thread.join()
的返回。 -
传递性: 如果 A happens-before B,且 B happens-before C,则 A happens-before C。
3. volatile 关键字
volatile
关键字提供了一种轻量级的同步机制,确保变量的更新操作对所有线程可见。使用 volatile
变量时,JMM 保证:
-
对
volatile
变量的写操作会立刻刷新到主内存中。 -
对
volatile
变量的读操作会从主内存中重新加载最新的值。 -
对
volatile
变量的读写操作不会与其他内存操作发生重排序。
4. 锁和同步
锁和同步是 JMM 确保内存可见性和指令顺序的核心机制:
synchronized 关键字
synchronized
关键字用于方法或代码块,确保在同一时刻只有一个线程能够执行同步代码。它隐含了两个 happens-before 规则:
-
对一个锁的解锁 happens-before 于后续对这个锁的加锁。
-
进入一个
synchronized
代码块前,必须获得锁,而退出这个代码块时必须释放锁。
Lock 接口
Java 提供了 java.util.concurrent.locks.Lock
接口,比 synchronized
更灵活。常用的实现包括 ReentrantLock
和 ReadWriteLock
。这些锁也遵循 happens-before 规则,确保线程间的内存可见性和操作顺序。
5. 内存屏障
内存屏障(Memory Barrier)是一种硬件指令,用于控制指令执行的顺序和内存操作的可见性。JMM 使用内存屏障来实现 volatile 变量的语义和锁的语义。常见的内存屏障有:
-
LoadLoad Barrier: 确保在屏障前的所有读操作都完成后,才能执行屏障后的读操作。
-
StoreStore Barrier: 确保在屏障前的所有写操作都完成后,才能执行屏障后的写操作。
-
LoadStore Barrier: 确保在屏障前的所有读操作都完成后,才能执行屏障后的写操作。
-
StoreLoad Barrier: 确保在屏障前的所有写操作都完成后,才能执行屏障后的读操作。这是最严格的内存屏障。
6. JMM 对指令重排序的限制
JMM 对指令重排序的限制主要通过以下方式实现:
-
编译器在编译阶段对代码进行重排序,但必须遵守 happens-before 规则。
-
处理器在执行阶段对指令进行重排序,但必须遵守内存屏障的约束。
-
Java 内存模型通过对 volatile 变量和同步块的使用来强制内存屏障,确保内存可见性和指令顺序。
7. 实践中的应用
volatile 的使用场景
-
状态标志:用 volatile 变量作为状态标志,确保线程能够及时看到状态的变化。
-
单例模式:使用 volatile 变量来实现线程安全的懒汉式单例模式。
public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;} }
synchronized 的使用场景
-
同步方法或代码块:在多线程环境中保护共享资源,确保线程安全。
-
经典生产者-消费者模型:使用 synchronized 实现线程间的协调。
class ProducerConsumer {private final LinkedList<Integer> list = new LinkedList<>();private final int CAPACITY = 10;public void produce() throws InterruptedException {int value = 0;while (true) {synchronized (this) {while (list.size() == CAPACITY) {wait();}list.add(value++);notify();}}}public void consume() throws InterruptedException {while (true) {synchronized (this) {while (list.isEmpty()) {wait();}int value = list.removeFirst();notify();}}} }
总结
Java 内存模型(JMM)通过定义内存可见性和指令重排序的规则,确保了多线程环境下程序的正确性和一致性。理解 JMM 有助于编写高效、安全的并发程序,避免常见的并发问题。