JUC知识点总结

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;
    }
​
    @Override
    public 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;
    }
​
    @Override
    public 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;
    }
​
    @Override
    public 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;
    }
​
    @Override
    public 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;
    }
​
    @Override
    public 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;
    }
​
    @Override
    protected 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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 框架提供了一种将任务提交与任务执行分离的方法。核心接口是 ExecutorExecutorService

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 {
    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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

FutureCallable 提供了一种异步获取结果的方式。

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> {
    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    public 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;
    }

    @Override
    protected 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. 基础概念

  1. 什么是线程安全?如何保证线程安全?

    • 线程安全是指多个线程访问共享资源时,不会导致数据不一致或程序异常。可以通过同步机制(如 synchronized 关键字、Lock 接口)或使用线程安全的类(如 ConcurrentHashMap)来保证线程安全。

  2. 什么是死锁?如何避免死锁?

    • 死锁是指两个或多个线程相互等待对方释放资源,导致程序无法继续执行。避免死锁的方法包括资源排序法、定时锁尝试以及使用高层次的锁机制(如 ReentrantLock)。

2. JUC 工具类

  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(() -> {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    latch.countDown();
                }).start();
            }
    
            latch.await();
            System.out.println("All tasks completed.");
        }
    }
  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(() -> {
                    try {
                        Thread.sleep(1000);
                        System.out.println(Thread.currentThread().getName() + " reached barrier.");
                        barrier.await();
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
        }
    }
  3. 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. 线程池

  1. 线程池的工作原理是什么?常见的线程池有哪些?

    • 线程池通过重用已创建的线程来减少线程创建和销毁的开销。常见的线程池包括 FixedThreadPoolCachedThreadPoolScheduledThreadPoolSingleThreadExecutor

  2. 如何自定义线程池?

    • 可以使用 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. 原子类和锁

  1. 什么是 CAS?CAS 是如何实现线程安全的?

    • CAS(Compare-And-Swap)是一种原子操作,用于实现无锁并发。它通过比较内存中的值和预期值,如果相等则更新为新值,否则继续重试。

  2. ReentrantLocksynchronized 的区别?

    • ReentrantLock 提供了比 synchronized 更加灵活的锁机制,支持公平锁、非公平锁以及在同一个线程中可以多次获取锁。synchronized 是 Java 内置的同步机制,使用简单但功能有限。

5. 并发编程中的经典问题

  1. 生产者-消费者问题如何解决?

    • 可以使用 BlockingQueueSemaphore 或者自定义锁和条件变量来解决生产者-消费者问题。

  2. 哲学家就餐问题如何解决?

    • 可以使用 ReentrantLock 或者 Semaphore 来解决哲学家就餐问题,避免死锁和饥饿问题。

继续介绍一些关于 JUC 的常见面试题和相关概念,帮助你更好地准备面试。

6. 高级锁机制

6.1 ReadWriteLock 的使用场景和实现原理
  • ReadWriteLock 允许多个读线程同时访问,但写线程独占访问,适用于读多写少的场景。

  • ReentrantReadWriteLockReadWriteLock 的一个实现,提供了 readLockwriteLock

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 框架用于将大任务拆分成多个小任务并行执行,适用于计算密集型任务。

  • 通过 ForkJoinPoolRecursiveTaskRecursiveAction 实现。

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;
    }

    @Override
    protected 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 对象在内存中的布局主要包括三个部分:

  1. 对象头(Object Header)

  2. 实例数据(Instance Data)

  3. 对齐填充(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 中的锁机制主要包括以下几种:

  1. 无锁(No Lock)

  2. 偏向锁(Biased Locking)

  3. 轻量级锁(Lightweight Locking)

  4. 重量级锁(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 更灵活。常用的实现包括 ReentrantLockReadWriteLock。这些锁也遵循 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 有助于编写高效、安全的并发程序,避免常见的并发问题。

  • 7
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值