JUC工具类

JUC工具类

JUC工具类:

  • CountDownLatch详解;
  • CyclicBarrier详解;
  • Semaphore详解;
  • Phaser详解;
  • Exchanger详解;
  • ThreadLocal详解;

1、Java中CountDownLatch详解

CountDownLatch​ 是 Java 中的一个同步辅助类,用于实现线程间的等待/通知机制。它通过一个计数器来控制线程的执行顺序,允许一个或多个线程等待其他线程执行完毕后再继续执行。

CountDownLatch​ 的主要特点包括:

  1. **计数器:**​CountDownLatch​ 使用一个计数器来进行控制,该计数器可以初始化一个整数值,表示需要等待的线程数量。
  2. **等待:**​CountDownLatch​ 提供了 await()​ 方法用于等待计数器归零,即等待其他线程执行完毕。线程调用 await()​ 方法后,如果计数器为零,则立即返回;如果计数器不为零,则线程会被阻塞,直到计数器归零。
  3. **减计数:**​CountDownLatch​ 提供了 countDown()​ 方法用于减少计数器的值,表示某个线程已经执行完毕。每次调用 countDown()​ 方法,计数器的值会减少1。

CountDownLatch​ 主要用于以下场景:

  1. 多线程协同工作: 当某个任务需要等待其他多个任务执行完毕后才能继续执行时,可以使用 CountDownLatch​ 进行线程的等待和通知。
  2. 线程同步: 在多线程环境下,可以使用 CountDownLatch​ 来控制线程的执行顺序,确保某些线程在其他线程完成特定操作后再执行。
  3. 性能测试: 在性能测试中,可以使用 CountDownLatch​ 来控制并发线程的启动和停止,以便对系统的性能进行评估和测试。

下面是一个简单的 CountDownLatch​ 使用案例:

CountDownLatch多线程任务协同工作

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample1 {
    public static void main(String[] args) throws InterruptedException {
        int numThreads = 3; // 总共需要等待的线程数量
        CountDownLatch latch = new CountDownLatch(numThreads);

        // 创建并启动多个线程
        for (int i = 0; i < numThreads; i++) {
            Thread thread = new Thread(new WorkerThread(latch));
            thread.start();
        }

        System.out.println("主线程在等待所有工作线程完成...");
        latch.await(); // 主线程等待所有工作线程执行完毕
        System.out.println("所有工作线程已完成,主线程继续执行...");
    }

    static class WorkerThread implements Runnable {
        private CountDownLatch latch;

        public WorkerThread(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " 开始执行");
            // 模拟工作线程执行耗时操作
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " 执行完毕");
            latch.countDown(); // 每个工作线程执行完毕后减少计数器的值
        }
    }
}

CountDownLatch线程同步案例2

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int numThreads = 3; // 总共有3个线程需要同步
        CountDownLatch latch = new CountDownLatch(numThreads); // 设置计数器初始值为3

        // 启动多个线程
        for (int i = 1; i <= numThreads; i++) {
            Thread thread = new Thread(new WorkerThread("Thread " + i, latch));
            thread.start();
        }

        System.out.println("主线程等待所有线程执行完毕...");
        latch.await(); // 主线程等待所有线程执行完毕
        System.out.println("所有线程已执行完毕,主线程继续执行...");
    }

    static class WorkerThread implements Runnable {
        private String name;
        private CountDownLatch latch;

        public WorkerThread(String name, CountDownLatch latch) {
            this.name = name;
            this.latch = latch;
        }

        @Override
        public void run() {
            System.out.println(name + " 开始执行");
            // 模拟线程执行耗时操作
            try {
                Thread.sleep((long) (Math.random() * 5000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(name + " 执行完毕");
            latch.countDown(); // 每个线程执行完毕后减少计数器的值
        }
    }
}

在上面的示例中,有3个工作线程(Thread 1、Thread 2、Thread 3)需要执行,主线程通过 CountDownLatch​ 等待所有工作线程执行完毕。每个工作线程执行完毕后都会调用 countDown()​ 方法来减少计数器的值,当计数器的值减为0时,主线程被唤醒,继续执行。这样就实现了多个线程之间的同步。


使用CountDownLatch性能测试案例

CountDownLatch​ 可以用于性能测试中,例如模拟多个线程并发执行某个任务,然后统计所有线程完成任务所花费的时间。下面是一个简单的示例,展示了如何使用 CountDownLatch​ 进行性能测试。

import java.util.concurrent.CountDownLatch;

public class PerformanceTest {
    public static void main(String[] args) throws InterruptedException {
        int numThreads = 10; // 总共有10个线程并发执行任务
        CountDownLatch startLatch = new CountDownLatch(1); // 启动门
        CountDownLatch endLatch = new CountDownLatch(numThreads); // 结束门

        // 启动多个线程并发执行任务
        for (int i = 1; i <= numThreads; i++) {
            Thread thread = new Thread(new WorkerThread("Thread " + i, startLatch, endLatch));
            thread.start();
        }

        // 主线程启动所有工作线程
        System.out.println("主线程启动所有工作线程...");
        long startTime = System.currentTimeMillis();
        startLatch.countDown(); // 启动门减少计数器的值,所有工作线程开始执行

        // 主线程等待所有工作线程执行完毕
        endLatch.await(); // 结束门等待所有工作线程执行完毕
        long endTime = System.currentTimeMillis();
        System.out.println("所有工作线程执行完毕,总共花费时间: " + (endTime - startTime) + " ms");
    }

    static class WorkerThread implements Runnable {
        private String name;
        private CountDownLatch startLatch;
        private CountDownLatch endLatch;

        public WorkerThread(String name, CountDownLatch startLatch, CountDownLatch endLatch) {
            this.name = name;
            this.startLatch = startLatch;
            this.endLatch = endLatch;
        }

        @Override
        public void run() {
            try {
                System.out.println(name + " 等待启动...");
                startLatch.await(); // 等待启动门打开,所有工作线程同时开始执行
                System.out.println(name + " 开始执行");
                // 模拟线程执行耗时操作
                Thread.sleep((long) (Math.random() * 5000));
                System.out.println(name + " 执行完毕");
                endLatch.countDown(); // 每个工作线程执行完毕后减少结束门的计数器
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在上面的示例中,有10个工作线程并发执行任务,主线程通过 CountDownLatch​ 控制所有工作线程的启动和结束。通过统计所有工作线程完成任务所花费的时间,可以进行性能测试和性能优化。


2、Java中CyclicBarrier详解

CyclicBarrier​ 是 Java 中的一个同步工具类,用于实现多个线程之间的同步等待。它的特点是可以在多个线程之间设置一个栅栏(Barrier),当所有线程都到达栅栏时,栅栏会自动打开,所有线程可以继续执行下一步操作。CyclicBarrier​ 可以用于线程之间的协同工作,特别适用于多个线程需要等待其他线程完成某个任务后再一起继续执行的场景。

CyclicBarrier​ 的主要特点包括:

  1. 可以设定等待的线程数量,当等待的线程数量满足条件时,栅栏会自动打开,所有线程可以继续执行下一步操作。
  2. 可以设置一个可选的回调函数,当栅栏打开时,会自动调用回调函数,用于执行特定的逻辑。
  3. 可以重复使用,当栅栏打开后,可以通过重置栅栏状态再次使用。
  4. 可以设置超时时间,当等待的线程数量未满足条件时,可以设置超时时间,超时后栅栏会自动打开。

下面是 CyclicBarrier​ 的一些常用方法:

  • CyclicBarrier(int parties)​:创建一个 CyclicBarrier​ 实例,设置等待的线程数量。
  • CyclicBarrier(int parties, Runnable barrierAction)​:创建一个 CyclicBarrier​ 实例,设置等待的线程数量和栅栏打开时执行的回调函数。
  • int await()​:等待所有线程到达栅栏,如果线程数量未满足条件,则当前线程会被阻塞。
  • int await(long timeout, TimeUnit unit)​:等待所有线程到达栅栏,如果线程数量未满足条件,可以设置超时时间。
  • void reset()​:重置栅栏状态,可以继续使用。

Java中CyclicBarrier的使用场景分析

CyclicBarrier​ 可以在以下场景中使用:

  1. 多线程任务的协同工作:当多个线程需要等待其他线程完成某个任务后再一起继续执行下一步操作时,可以使用 CyclicBarrier​。例如,一个大型任务需要被拆分成多个子任务,并且这些子任务需要并行执行,但在进行下一步之前需要等待所有子任务完成,可以使用 CyclicBarrier​ 来实现线程之间的协同工作。
  2. 并行计算的结果合并:当多个线程分别计算某个问题的一部分,然后将结果合并后再继续执行下一步操作时,可以使用 CyclicBarrier​。例如,一个数据处理任务被拆分成多个子任务,每个子任务在独立的线程中执行,然后将计算结果合并后再进行后续处理,可以使用 CyclicBarrier​ 来实现结果的合并操作。
  3. 多阶段并发任务:当一个并发任务分为多个阶段,每个阶段需要等待其他阶段的线程完成后再继续执行时,可以使用 CyclicBarrier​。例如,一个多阶段的流水线任务,每个阶段有不同的线程执行,需要等待上一阶段的线程完成后再进行下一阶段的操作,可以使用 CyclicBarrier​ 来实现不同阶段之间的同步。
  4. 测试用例的并发控制:当需要同时启动多个线程执行测试用例,但需要等待所有测试用例执行完成后再进行结果统计或其他操作时,可以使用 CyclicBarrier​。例如,一个并发性能测试,需要同时启动多个线程模拟用户请求,然后等待所有线程完成后再进行性能统计,可以使用 CyclicBarrier​ 来控制并发测试的同步。
  5. 并行化数据处理:当需要对大量数据进行并行处理,例如数据分析、数据处理等场景时,可以使用 CyclicBarrier​ 来实现并行化的数据处理,提高处理速度和效率。

总的来说,CyclicBarrier​ 适用于多线程之间需要协同工作、同步等待的场景,可以帮助多个线程在满足特定条件时进行同步,从而实现并行计算、任务协同工作等并发编程的需求。


CyclicBarrier的使用案例

下面是一个简单的使用 CyclicBarrier​ 的案例,展示了多个线程在到达屏障点后进行同步等待的过程:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    private static final int THREADS = 3;

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(THREADS, () -> {
            System.out.println("所有线程已经到达屏障点,开始执行后续操作。");
        });

        for (int i = 0; i < THREADS; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 开始执行任务。");
                try {
                    Thread.sleep(2000); // 模拟任务执行耗时
                    System.out.println(Thread.currentThread().getName() + " 到达屏障点。");
                    cyclicBarrier.await(); // 等待其他线程到达屏障点
                    System.out.println(Thread.currentThread().getName() + " 继续执行后续操作。");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

在这个例子中,我们创建了一个 CyclicBarrier​ 实例,设置屏障点为 3,表示需要等待 3 个线程都到达屏障点后才能继续执行后续操作。然后创建了 3 个线程,每个线程都会执行一段模拟耗时的任务,然后调用 await()​ 方法等待其他线程到达屏障点。当 3 个线程都到达屏障点后,会触发 CyclicBarrier​ 中设置的回调方法执行后续操作。

这个案例中演示了 CyclicBarrier​ 的基本使用方式,通过设置屏障点和调用 await()​ 方法来实现线程之间的同步等待,以便在所有线程都到达屏障点后再一起继续执行后续操作。


3、Java中Semaphore详解

Semaphore​ 是 Java 中的一种同步工具,用于控制同时访问某资源的线程数量。它基于计数器的方式,可以设置允许同时访问资源的线程数量,当达到限制时,其他线程需要等待。Semaphore​ 提供了两种操作:acquire()​ 用于申请资源,release()​ 用于释放资源。

Semaphore​ 类的主要构造方法如下:

public Semaphore(int permits)
public Semaphore(int permits, boolean fair)

其中,permits​ 参数表示允许同时访问资源的线程数量,fair​ 参数表示是否使用公平性策略,如果设置为 true​,则线程会按照申请的顺序获得许可,遵循先进先出(FIFO)的规则;如果设置为 false​,则线程获得许可的顺序不受保证。

Semaphore​ 类的常用方法如下:

  • void acquire()​: 申请一个许可,如果没有许可可用,则线程会阻塞,直到获得许可。
  • void acquire(int permits)​: 申请指定数量的许可,如果没有足够的许可可用,则线程会阻塞,直到获得足够的许可。
  • void release()​: 释放一个许可。
  • void release(int permits)​: 释放指定数量的许可。
  • int availablePermits()​: 获取当前可用的许可数量。
  • int getQueueLength()​: 获取等待许可的线程数量。

下面是一个简单的使用 Semaphore​ 的案例,展示了如何使用 Semaphore​ 控制同时访问某资源的线程数量:

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    private static final int THREADS = 5;
    private static final int MAX_CONCURRENT = 2;

    private static Semaphore semaphore = new Semaphore(MAX_CONCURRENT);

    public static void main(String[] args) {
        for (int i = 0; i < THREADS; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 正在执行任务。");
                    semaphore.acquire(); // 申请许可
                    System.out.println(Thread.currentThread().getName() + " 获得许可,开始访问资源。");
                    // 访问资源的代码
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放许可
                    System.out.println(Thread.currentThread().getName() + " 释放许可,结束任务。");
                }
            }).start();
        }
    }
}

Semaphore的使用场景有哪些?

Semaphore​ 是一种用于控制并发访问资源数量的同步工具,可以用于多种场景。以下是一些常见的 Semaphore​ 的使用场景:

  1. 限制并发访问数量:可以使用 Semaphore​ 来限制同时访问某个共享资源的线程数量,从而控制并发访问的并发度。例如,可以用于限制同时访问数据库连接池、线程池、网络连接等资源的线程数量,以避免资源过度竞争导致性能下降。
  2. 控制资源分配:Semaphore​ 可以用于控制对有限资源的分配。例如,可以使用 Semaphore​ 来控制对某个有限数量的许可证的分配,用于控制某个服务的并发调用数量、限制某个资源的消耗速率等。
  3. 实现互斥锁:Semaphore​ 可以用于实现互斥锁的功能,通过设置许可数量为1,确保只有一个线程可以获得许可执行临界区代码,从而实现互斥的效果。
  4. 同步多个线程的执行:Semaphore​ 可以用于同步多个线程的执行,例如在多个线程之间达成某种协议或条件。可以通过设置不同的许可数量来控制线程的执行顺序、数量、等待时间等。
  5. 控制流量:Semaphore​ 可以用于控制流量的大小,例如限制请求的并发数、控制消息队列的大小、限制文件的并发写操作等。

需要注意的是,Semaphore​ 并不是适用于所有情况的同步工具,使用时需要仔细考虑业务场景和并发需求,确保正确使用。同时,Semaphore​ 的使用应遵循线程安全的编程原则,避免出现竞态条件和死锁等问题。


Semaphore限制并发访问数量的案例

下面是一个使用 Semaphore​ 来限制并发访问数量的案例,假设有一个共享资源,例如数据库连接池,需要限制同时访问的线程数量为固定值:

import java.util.concurrent.Semaphore;

public class DatabaseConnectionPool {
    private final int MAX_CONNECTIONS = 10; // 最大连接数
    private final Semaphore semaphore = new Semaphore(MAX_CONNECTIONS); // 使用Semaphore限制并发访问数量
    private final Connection[] connections; // 假设连接池中的连接是Connection类型的对象数组

    public DatabaseConnectionPool() {
        connections = new Connection[MAX_CONNECTIONS];
        // 初始化连接池
        for (int i = 0; i < MAX_CONNECTIONS; i++) {
            connections[i] = new Connection(); // 假设Connection是一个自定义的数据库连接类
        }
    }

    public Connection getConnection() throws InterruptedException {
        semaphore.acquire(); // 获取许可证,如果没有可用的许可证,则阻塞等待
        return getAvailableConnection();
    }

    public void releaseConnection(Connection connection) {
        releaseConnection(connection.getId()); // 释放连接
    }

    public void releaseConnection(int connectionId) {
        // 释放连接,将许可证归还给Semaphore
        semaphore.release();
    }

    private Connection getAvailableConnection() {
        // 获取一个可用的连接
        // 省略实现细节
        return null;
    }

    private static class Connection {
        // 自定义的数据库连接类
        // 省略实现细节
        private int id; // 假设有一个连接ID用于标识连接

        public int getId() {
            return id;
        }
    }
}

在上面的案例中,Semaphore​ 被用于限制同时访问数据库连接池的线程数量,最大限制为 MAX_CONNECTIONS​。当获取连接时,如果没有可用的连接,线程将被阻塞等待直到有可用的连接并获取到许可证。当释放连接时,许可证将归还给 Semaphore​,从而允许其他线程获取连接。这样可以有效地控制并发访问数据库连接池的线程数量,防止过度竞争导致性能下降。


Semaphore控制资源分配案例

下面是一个使用 Semaphore​ 来控制资源分配的案例,假设有一个固定数量的资源(如打印机),多个线程需要申请资源并使用,但同时只能有限定数量的线程能够获取到资源:

import java.util.concurrent.Semaphore;

public class PrinterPool {
    private final int MAX_PRINTERS = 3; // 最大打印机数量
    private final Semaphore semaphore = new Semaphore(MAX_PRINTERS); // 使用Semaphore限制资源分配
    private final Printer[] printers; // 假设打印机资源是Printer类型的对象数组

    public PrinterPool() {
        printers = new Printer[MAX_PRINTERS];
        // 初始化打印机资源
        for (int i = 0; i < MAX_PRINTERS; i++) {
            printers[i] = new Printer(i); // 假设Printer是一个自定义的打印机类
        }
    }

    public Printer getPrinter() throws InterruptedException {
        semaphore.acquire(); // 获取许可证,如果没有可用的许可证,则阻塞等待
        return getAvailablePrinter();
    }

    public void releasePrinter(Printer printer) {
        releasePrinter(printer.getId()); // 释放打印机资源
    }

    public void releasePrinter(int printerId) {
        // 释放打印机资源,将许可证归还给Semaphore
        semaphore.release();
    }

    private Printer getAvailablePrinter() {
        // 获取一个可用的打印机
        // 省略实现细节
        return null;
    }

    private static class Printer {
        // 自定义的打印机类
        // 省略实现细节
        private int id; // 假设有一个打印机ID用于标识打印机

        public Printer(int id) {
            this.id = id;
        }

        public int getId() {
            return id;
        }
    }
}

在上面的案例中,Semaphore​ 被用于控制资源分配,最大限制为 MAX_PRINTERS​,即同时最多允许 MAX_PRINTERS​ 个线程获取到资源。当申请资源时,如果没有可用的资源,线程将被阻塞等待直到有可用的资源并获取到许可证。当释放资源时,许可证将归还给 Semaphore​,从而允许其他线程获取资源。这样可以有效地控制资源的分配,防止资源过度竞争导致资源耗尽或性能下降。


Semaphore同步多个线程的执行

Semaphore​ 可以用于同步多个线程的执行,以下是一个示例:

import java.util.concurrent.Semaphore;

public class MultiThreadExecutor {
    private final Semaphore semaphore = new Semaphore(0); // 初始化Semaphore,设置许可证数量为0
    private final int THREAD_COUNT = 5; // 线程数量
    private final Runnable task = () -> {
        try {
            System.out.println("Thread " + Thread.currentThread().getId() + " is running.");
            Thread.sleep(1000); // 模拟线程执行任务
            System.out.println("Thread " + Thread.currentThread().getId() + " finished.");
            semaphore.release(); // 任务完成后释放一个许可证
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    };

    public void execute() throws InterruptedException {
        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(task).start(); // 启动多个线程执行任务
        }

        semaphore.acquire(THREAD_COUNT); // 等待所有线程任务完成,一共需要获取 THREAD_COUNT 个许可证
        System.out.println("All threads finished.");
    }

    public static void main(String[] args) {
        MultiThreadExecutor executor = new MultiThreadExecutor();
        try {
            executor.execute(); // 启动线程池执行任务
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在上面的例子中,Semaphore​ 被用于同步多个线程的执行。线程池中的每个线程执行任务时,会获取一个许可证,表示任务正在执行。当线程完成任务后,会释放一个许可证。在 execute()​ 方法中,我们使用 semaphore.acquire(THREAD_COUNT)​ 来等待所有线程任务完成,需要获取 THREAD_COUNT​ 个许可证,表示所有线程都已经完成任务。一旦所有线程都完成了任务,semaphore.acquire(THREAD_COUNT)​ 将返回,然后主线程继续执行后续操作。这样可以确保多个线程的任务都完成后再继续执行主线程的后续逻辑。


Semaphore控制流量案例

Semaphore​ 可以用于控制流量,限制系统中某一资源的并发访问数量。以下是一个示例:

import java.util.concurrent.Semaphore;

public class TrafficController {
    private final Semaphore semaphore = new Semaphore(3); // 设置许可证数量为3,表示最多允许3辆车同时通过

    public void enterIntersection(String carName) throws InterruptedException {
        System.out.println(carName + " is waiting to enter the intersection.");
        semaphore.acquire(); // 获取一个许可证,表示车辆进入了路口
        System.out.println(carName + " enters the intersection.");
        Thread.sleep(1000); // 模拟车辆通过路口的时间
        semaphore.release(); // 释放一个许可证,表示车辆离开了路口
        System.out.println(carName + " leaves the intersection.");
    }

    public static void main(String[] args) {
        TrafficController trafficController = new TrafficController();
        // 启动多个线程,模拟多辆车同时访问路口
        for (int i = 1; i <= 5; i++) {
            final String carName = "Car " + i;
            new Thread(() -> {
                try {
                    trafficController.enterIntersection(carName);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

在上面的例子中,Semaphore​ 用于控制同时通过路口的车辆数量,设置许可证数量为3,表示最多允许3辆车同时通过。每个车辆线程在进入路口时先尝试获取一个许可证,如果成功获取到许可证,则表示可以进入路口。车辆线程通过路口后,释放许可证,表示离开了路口,从而允许其他车辆线程继续获取许可证进入路口。这样就实现了对流量的控制,限制了同时通过路口的车辆数量。


4、Java中Phaser详解

Phaser​ 是 Java 并发包中的一个同步器,用于协调多个线程的执行。它提供了一种更灵活、更高级的同步机制,用于控制线程的阶段性执行。

Phaser​ 提供了类似于 CyclicBarrier​ 的功能,但它的使用更加灵活,可以适应更复杂的同步场景。Phaser​ 可以用于控制一组线程在到达某个共同的同步点之前等待,然后一起继续执行;也可以用于控制一组线程在某个共同的同步点之后继续执行。Phaser​ 还可以动态地注册和注销线程,灵活地调整同步参与者的数量。

Phaser​ 的核心概念是 “phase”(阶段)和 “party”(参与者)。一个 Phaser​ 可以有多个阶段,每个阶段都有多个参与者。线程在到达阶段同步点时可以等待其他线程到达,然后一起继续执行;在下一个阶段开始时,线程可以继续执行或者等待其他线程执行完毕。

以下是一个简单的 Phaser​ 使用案例:

import java.util.concurrent.Phaser;

public class PhaserExample {
    public static void main(String[] args) {
        Phaser phaser = new Phaser(3); // 创建一个 Phaser,设置参与者数量为3

        for (int i = 0; i < 3; i++) {
            new Thread(new Worker(phaser)).start(); // 创建3个线程,并启动它们
        }
    }

    static class Worker implements Runnable {
        private final Phaser phaser;

        public Worker(Phaser phaser) {
            this.phaser = phaser;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " starts working.");
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + " starts phase " + (i + 1));
                phaser.arriveAndAwaitAdvance(); // 到达阶段同步点并等待其他线程到达
                System.out.println(Thread.currentThread().getName() + " continues working in phase " + (i + 1));
            }
            System.out.println(Thread.currentThread().getName() + " finishes working.");
        }
    }
}

Phaser​ 是 Java 并发包中的一个同步器,用于协调多个线程的执行。它提供了一种更灵活、更高级的同步机制,用于控制线程的阶段性执行。

Phaser​ 提供了类似于 CyclicBarrier​ 的功能,但它的使用更加灵活,可以适应更复杂的同步场景。Phaser​ 可以用于控制一组线程在到达某个共同的同步点之前等待,然后一起继续执行;也可以用于控制一组线程在某个共同的同步点之后继续执行。Phaser​ 还可以动态地注册和注销线程,灵活地调整同步参与者的数量。

Phaser​ 的核心概念是 “phase”(阶段)和 “party”(参与者)。一个 Phaser​ 可以有多个阶段,每个阶段都有多个参与者。线程在到达阶段同步点时可以等待其他线程到达,然后一起继续执行;在下一个阶段开始时,线程可以继续执行或者等待其他线程执行完毕。

以下是一个简单的 Phaser​ 使用案例:

 
   
   
   
   
java Copy code
import java.util.concurrent.Phaser; public class PhaserExample { public static void main(String[] args) { Phaser phaser = new Phaser(3); // 创建一个 Phaser,设置参与者数量为3 for (int i = 0; i < 3; i++) { new Thread(new Worker(phaser)).start(); // 创建3个线程,并启动它们 } } static class Worker implements Runnable { private final Phaser phaser; public Worker(Phaser phaser) { this.phaser = phaser; } @Override public void run() { System.out.println(Thread.currentThread().getName() + " starts working."); for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + " starts phase " + (i + 1)); phaser.arriveAndAwaitAdvance(); // 到达阶段同步点并等待其他线程到达 System.out.println(Thread.currentThread().getName() + " continues working in phase " + (i + 1)); } System.out.println(Thread.currentThread().getName() + " finishes working."); } } }

在上面的例子中,创建了一个 Phaser​ 对象,设置参与者数量为3,表示有3个线程参与同步。每个线程在运行时通过 phaser.arriveAndAwaitAdvance()​ 方法到达阶段同步点,并等待其他线程到达。当所有线程都到达阶段同步点后,它们一起继续执行,然后进入下一个阶段,直到完成所有阶段的执行。


Phaser的使用场景分析

Phaser​ 可以在以下场景中使用:

  1. 阶段性任务的并行执行:Phaser​ 可以用于协调一组线程在完成某个阶段的任务后,一起进入下一个阶段并继续执行。这在一些需要分阶段进行任务的并行计算中很有用,例如大规模数据处理、图像处理、模拟等场景。
  2. 动态线程数的控制:Phaser​ 允许线程在任何时候动态地注册和注销,从而可以灵活地调整参与同步的线程数量。这在一些需要根据实际情况动态控制线程数量的场景中非常有用,例如线程池的动态调整、动态任务分配等。
  3. 多线程协作的复杂场景:Phaser​ 提供了更灵活和高级的同步机制,可以在复杂的场景中实现多线程的协作。例如,在一些多阶段、多任务、多条件的场景中,Phaser​ 可以用于更加灵活和高效地管理线程的同步和协作。
  4. 公平性控制:与其他同步器不同,Phaser​ 具有公平性控制的特性,可以通过 Phaser​ 的构造函数参数来设置公平性。这在一些需要公平性控制的场景中很有用,例如任务调度、资源分配等。
  5. 可扩展的同步器:Phaser​ 提供了可以扩展的同步器接口,可以通过继承和扩展 Phaser​ 来实现自定义的同步逻辑,从而满足特定的同步需求。

总的来说,Phaser​ 在复杂的多线程协作场景中非常有用,提供了灵活、高级和可扩展的同步机制,用于协调多个线程的执行,并可以根据实际需求进行动态的线程管理。


5、Java中Exchanger详解

简介

Exchanger​ 是 Java 中的一个同步工具类,用于在两个线程之间进行数据交换。Exchanger​ 提供了一个同步点,当两个线程都到达这个同步点时,它们可以交换数据。

Exchanger​ 的主要特点包括:

  1. 线程间数据交换:Exchanger​ 允许两个线程之间进行数据的交换,其中一个线程将数据放入 Exchanger​,而另一个线程从 Exchanger​ 中取出数据,从而实现线程间的数据传递。
  2. 同步点控制:Exchanger​ 提供了一个同步点,当两个线程都到达这个同步点时,它们可以交换数据。如果一个线程先到达同步点,它将等待另一个线程到达,直到两个线程都到达同步点,数据才会被交换。
  3. 线程安全:Exchanger​ 是线程安全的,可以被多个线程同时使用。
  4. 数据传递控制:Exchanger​ 允许控制数据传递的方式,可以选择是一对一的传递还是多对一的传递。

Exchanger​ 的使用场景包括但不限于以下情况:

  1. 线程间数据交换:Exchanger​ 可以用于两个线程之间交换数据,例如线程 A 生成数据,线程 B 处理数据,通过 Exchanger​ 可以实现数据的传递和交换。
  2. 数据流控制:Exchanger​ 可以用于控制数据的流量,例如限制某个线程生成数据的速度,只有当另一个线程处理完数据后才能继续生成数据。
  3. 线程间协作:Exchanger​ 可以用于线程间的协作,例如一个线程在某个条件满足时将数据传递给另一个线程,从而实现线程间的协作和同步。

Java中Exchanger的使用案例

以下是一个简单的 Exchanger​ 使用案例,展示了两个线程之间的数据交换:

import java.util.concurrent.Exchanger;

public class ExchangerExample {
    public static void main(String[] args) {
        Exchanger exchanger = new Exchanger<>();

        Thread thread1 = new Thread(() -> {
            try {
                String data1 = "Data from Thread 1";
                System.out.println("Thread 1 sending data: " + data1);
                String data2 = exchanger.exchange(data1);
                System.out.println("Thread 1 received data: " + data2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                String data1 = "Data from Thread 2";
                System.out.println("Thread 2 sending data: " + data1);
                String data2 = exchanger.exchange(data1);
                System.out.println("Thread 2 received data: " + data2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread1.start();
        thread2.start();

        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,我们创建了一个 Exchanger​ 对象,并通过两个线程 thread1​ 和 thread2​ 进行数据交换。thread1​ 将字符串 “Data from Thread 1” 放入 Exchanger​,而 thread2​ 将字符串 “Data from Thread 2” 放入 Exchanger​。当两个线程都到达 Exchanger​ 的同步点时,它们会交换数据,thread1​ 取得了 thread2​ 放入的数据 “Data from Thread 2”,而 thread2​ 取得了 thread1​ 放入的数据 “Data from Thread 1”。这样,两个线程之间成功地进行了数据交换。注意,在 exchange()​ 方法中,如果一个线程先到达同步点,它将等待另一个线程到达,直到两个线程都到达同步点,数据才会被交换。


Exchanger在开发中的使用场景

Exchanger​ 是 Java 并发包中的一个工具类,用于在两个线程之间进行数据交换。它可以在多线程环境下实现线程之间的数据传递和同步,适用于以下一些场景:

  1. 线程间数据交换:Exchanger​ 可以用于两个线程之间进行数据交换,例如一个线程生成数据,另一个线程处理数据,通过 Exchanger​ 可以实现数据的传递和同步。
  2. 数据校对:在多线程环境下,多个线程生成数据,然后通过 Exchanger​ 进行数据校对,确保数据的一致性和准确性。
  3. 数据缓冲区:Exchanger​ 可以用于实现多线程之间的数据缓冲区,例如一个线程从外部读取数据放入缓冲区,另一个线程从缓冲区取出数据进行处理。
  4. 数据处理流程控制:Exchanger​ 可以用于控制多个线程之间的数据处理流程,例如一个线程负责数据的生成,另一个线程负责数据的处理,通过 Exchanger​ 进行数据的交换和同步,从而实现数据处理流程的控制和协调。

需要注意的是,Exchanger​ 在使用时需要谨慎,特别是在高并发环境下,避免出现死锁或其他线程同步问题。使用 Exchanger​ 时应注意合理设计线程的执行顺序和数据交换方式,确保线程之间能够正确地交换数据并保持同步。


6、Java中的ThreadLocal详解

简介

ThreadLocal​ 是 Java 中的一个线程局部变量类,它允许每个线程都有自己的变量副本,互不干扰。ThreadLocal​ 提供了一种简单的机制,可以在多线程环境下实现线程间的数据隔离,每个线程都可以访问和修改自己的线程局部变量,而不会影响其他线程的变量。

ThreadLocal​ 主要有以下几个特点:

  1. 线程隔离:每个线程都有自己的 ThreadLocal​ 变量副本,互不干扰,线程间的数据访问不会相互影响。
  2. 线程安全:ThreadLocal​ 提供了线程安全的方式来存储线程局部变量,避免了多线程并发访问的问题。
  3. 高效性:ThreadLocal​ 使用了线程的 ThreadLocalMap 来存储数据,具有较高的访问速度。

ThreadLocal​ 主要用于在多线程环境下实现线程间的数据隔离,常见的使用场景包括:

  1. 线程上下文信息传递:例如在一个 web 服务器中,每个请求都由一个独立的线程来处理,可以使用 ThreadLocal​ 将请求的上下文信息(如用户信息、请求参数等)保存在线程局部变量中,以便在整个请求处理过程中方便地访问。
  2. 共享资源的线程安全访问:例如多线程访问同一个数据库连接或者缓存对象时,可以使用 ThreadLocal​ 来保存每个线程的连接或者缓存对象,避免线程间的竞争和同步开销。
  3. 避免传递参数:例如在多层嵌套的方法调用中,需要将一些参数传递到内层的方法中,可以使用 ThreadLocal​ 将这些参数保存在线程局部变量中,从而避免传递参数的复杂性。

需要注意的是,虽然 ThreadLocal​ 提供了一种简单的线程间数据隔离的方式,但在使用时需要注意谨慎,避免滥用。应该合理使用 ThreadLocal​,并在使用后及时清理线程局部变量,以避免可能的内存泄漏问题。此外,由于 ThreadLocal​ 的使用涉及到线程间的数据共享和隔离,因此在使用时需要仔细考虑线程安全和并发性问题,确保多线程环境下的正确性和稳定性。


ThreadLocal的使用方法分析

在 Java 中,使用 ThreadLocal​ 主要涉及以下三个步骤:

  1. 创建 ThreadLocal​ 对象:可以通过直接实例化 ThreadLocal​ 类或者使用其子类 InheritableThreadLocal​ 创建一个 ThreadLocal​ 对象。例如:
ThreadLocal threadLocal = new ThreadLocal<>();
  1. 存储和获取线程局部变量:通过 ThreadLocal​ 对象的 set​ 方法来存储线程局部变量的值,通过 get​ 方法来获取线程局部变量的值。例如:
threadLocal.set(42); // 存储线程局部变量的值
Integer value = threadLocal.get(); // 获取线程局部变量的值

  1. 清理线程局部变量:由于 ThreadLocal​ 使用了线程的 ThreadLocalMap 来存储数据,它会伴随线程的生命周期一直存在,可能导致内存泄漏。因此,在不再使用线程局部变量时,应该及时清理,可以通过 remove​ 方法来清理线程局部变量的值。例如:
threadLocal.remove(); // 清理线程局部变量的值

以下是一个简单的示例,演示了如何使用 ThreadLocal​ 存储和获取线程局部变量的值:

 
   
   
   
   

public class ThreadLocalExample {

    private static ThreadLocal threadLocal = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        // 创建两个线程,并分别设置线程局部变量的值
        Thread thread1 = new Thread(() -> {
            threadLocal.set(1);
            System.out.println("Thread 1 - ThreadLocal value: " + threadLocal.get());
        });

        Thread thread2 = new Thread(() -> {
            threadLocal.set(2);
            System.out.println("Thread 2 - ThreadLocal value: " + threadLocal.get());
        });

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        // 获取主线程中的线程局部变量的值(应为 null)
        System.out.println("Main Thread - ThreadLocal value: " + threadLocal.get());
    }
}

运行以上示例,输出结果应为:

Thread 1 - ThreadLocal value: 1
Thread 2 - ThreadLocal value: 2
Main Thread - ThreadLocal value: null

从输出结果可以看出,每个线程都可以通过 ThreadLocal​ 获取自己的线程局部变量的值,互不干扰,而主线程中的线程局部变量的值为 null​,说明在主线程中并没有设置该线程局部变量的值。这说明了 ThreadLocal​ 的线程隔离性。


ThreadLocal的高级使用案例有哪些

除了基本的用法外,ThreadLocal​ 还可以用于一些高级的使用场景,包括但不限于以下几种:

  1. 线程上下文传递:ThreadLocal​ 可以用于在多个方法或组件之间传递线程上下文信息,避免显式传递参数或全局变量的方式。例如,在一个复杂的业务处理流程中,可以将某些需要在多个方法中共享的上下文信息,如请求信息、用户信息等,存储在 ThreadLocal​ 中,各个方法可以直接从 ThreadLocal​ 中获取这些信息,无需显式传递参数,从而简化了方法之间的调用。
public class ThreadContext {
    private static ThreadLocal userThreadLocal = new ThreadLocal<>();

    public static void setUser(User user) {
        userThreadLocal.set(user);
    }

    public static User getUser() {
        return userThreadLocal.get();
    }
}

public class UserService {
    public void processRequest(Request request) {
        // 从请求中获取用户信息
        User user = extractUserFromRequest(request);
        // 存储用户信息到ThreadLocal
        ThreadContext.setUser(user);
        // 执行业务处理
        // ...
        // 从ThreadLocal中获取用户信息
        User currentUser = ThreadContext.getUser();
        // ...
    }
}

  1. 线程资源管理:ThreadLocal​ 可以用于线程内的资源管理,例如数据库连接、线程池、缓存等。通过 ThreadLocal​ 可以在线程内维护一个线程独享的资源对象,避免资源对象的创建和销毁在多个线程之间的频繁切换,从而提高性能和减少资源竞争。
public class ConnectionManager {
    private static ThreadLocal connectionThreadLocal = new ThreadLocal<>();

    public static Connection getConnection() {
        Connection connection = connectionThreadLocal.get();
        if (connection == null) {
            // 创建数据库连接
            connection = createConnection();
            // 存储到ThreadLocal
            connectionThreadLocal.set(connection);
        }
        return connection;
    }

    public static void releaseConnection() {
        Connection connection = connectionThreadLocal.get();
        if (connection != null) {
            // 关闭数据库连接
            closeConnection(connection);
            // 从ThreadLocal中移除
            connectionThreadLocal.remove();
        }
    }
}
  1. 避免全局变量:ThreadLocal​ 可以用于避免全局变量的使用,从而减少全局变量带来的潜在风险,如线程安全性、可维护性等。通过将线程特定的状态存储在 ThreadLocal​ 中,避免了使用全局变量来共享状态的方式,从而简化了代码逻辑,降低了代码的复杂度。

下面是一个使用 ThreadLocal​ 避免全局变量的案例,该案例模拟了一个简单的请求处理流程,使用 ThreadLocal​ 存储请求信息,避免了全局变量的使用:

public class Request {
    private String requestId;
    private String requestContent;

    // 省略构造方法和getter/setter
}

public class RequestContext {
    private static ThreadLocal requestThreadLocal = new ThreadLocal<>();

    public static void setRequest(Request request) {
        requestThreadLocal.set(request);
    }

    public static Request getRequest() {
        return requestThreadLocal.get();
    }

    public static void clear() {
        requestThreadLocal.remove();
    }
}

public class RequestProcessor {
    public void process(Request request) {
        // 将请求信息存储到ThreadLocal
        RequestContext.setRequest(request);
        // 执行请求处理逻辑
        // ...
        // 从ThreadLocal中获取请求信息
        Request currentRequest = RequestContext.getRequest();
        // ...
        // 清除ThreadLocal中的请求信息
        RequestContext.clear();
    }
}

public class Main {
    public static void main(String[] args) {
        // 模拟多个请求处理线程
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                Request request = new Request();
                // 设置请求信息
                // ...
                RequestProcessor processor = new RequestProcessor();
                processor.process(request);
            }).start();
        }
    }
}

在这个案例中,RequestContext​ 类使用了 ThreadLocal​ 存储了请求信息,每个线程都可以通过 RequestContext.getRequest()​ 方法获取到自己线程内的请求信息,避免了使用全局变量的方式,从而简化了代码逻辑,降低了全局变量带来的潜在风险。同时,通过在每个请求处理流程中清除 ThreadLocal​ 中的请求信息,保证了线程之间的隔离性和线程安全性。


ThreadLocal的原理分析

ThreadLocal​ 是 Java 中的一个线程本地变量工具类,它提供了一种在多线程环境下,为每个线程独立维护变量副本的机制,从而实现了线程间的隔离。其原理主要涉及到 ThreadLocal 实例、Thread 对象以及线程之间的数据传递。

在 Java 中,每个线程都有一个 Thread 对象与之对应,Thread 对象中包含了一个 ThreadLocalMap 类型的成员变量 threadLocals,这个 threadLocals 是一个 ThreadLocal 实例的集合,其中 ThreadLocal 是一个弱引用(WeakReference)的键,而实际的变量副本则作为值存储在 ThreadLocalMap 中。ThreadLocalMap 是 ThreadLocal 的内部类,它实现了一个类似于 HashMap 的键值对结构,用于存储每个线程的 ThreadLocal 变量副本。

当我们调用 ThreadLocal 的 set​ 方法时,实际上是在当前线程的 Thread 对象的 threadLocals 中设置一个键值对,其中键为 ThreadLocal 实例,值为我们传入的变量。当我们调用 ThreadLocal 的 get​ 方法时,实际上是从当前线程的 Thread 对象的 threadLocals 中获取与当前 ThreadLocal 实例对应的变量副本。

需要注意的是,由于 ThreadLocal 使用了弱引用作为键,当线程结束时,Thread 对象被垃圾回收时,其 threadLocals 中对应的 ThreadLocal 实例也可能被回收,从而避免了内存泄漏的风险。

另外,ThreadLocal 还提供了 initialValue​ 方法,它可以被子类重写,用于指定在第一次访问 ThreadLocal 变量时,如果没有为该线程设置过值,应该返回的默认值。这个方法在每个线程第一次访问 ThreadLocal 变量时被调用,从而实现了每个线程在第一次访问 ThreadLocal 变量时都可以获取到一个初始化的值。

需要注意的是,虽然 ThreadLocal 可以解决多线程环境下的线程间隔离问题,但也需要小心使用,避免滥用。在使用 ThreadLocal 时,应该合理管理线程与变量之间的生命周期,避免潜在的内存泄漏问题,并确保在多线程环境下不会出现意外的数据共享或线程安全问题。


ThreadLocal使用注意事项

在使用 ThreadLocal 时,需要注意以下几个事项:

  1. 内存泄漏风险:由于 ThreadLocal 使用了弱引用作为键,当线程结束时,Thread 对象被垃圾回收时,其 threadLocals 中对应的 ThreadLocal 实例也可能被回收。但如果 ThreadLocal 实例被回收后,对应的变量副本没有被及时清理,就可能导致内存泄漏。因此,在使用 ThreadLocal 时,应该避免长时间持有对 ThreadLocal 实例的引用,以免导致内存泄漏。
  2. 线程安全性:ThreadLocal 主要用于实现线程间的隔离,每个线程都有自己的变量副本,从而避免了线程间的共享和竞争条件。但在使用 ThreadLocal 时,应注意变量副本之间的线程安全性。如果 ThreadLocal 中存储的是可变对象,并且多个线程对该对象进行修改,就需要额外考虑线程安全性,例如使用锁或其他同步机制。
  3. 生命周期管理:ThreadLocal 实例通常与线程的生命周期关联,因此需要谨慎管理 ThreadLocal 实例的生命周期。在线程结束后,应该及时清理 ThreadLocal 实例,以避免潜在的内存泄漏风险。可以使用 ThreadLocal 的 remove() 方法或者使用 Java 8 中引入的 ThreadLocal.withInitial()​ 方法来设置 ThreadLocal 的初始值,并且避免在使用 ThreadLocal 时出现空指针异常。
  4. 并发性能:虽然 ThreadLocal 可以提供线程间的隔离,但在高并发环境下,频繁的线程切换和变量副本的创建和销毁也可能导致性能开销。因此,在使用 ThreadLocal 时,应该合理评估并发性能,并在需要的情况下进行性能优化。
  5. 应用场景选择:ThreadLocal 应该被用于那些线程间需要隔离数据的场景,例如在多线程环境下的日志记录、用户身份认证信息、数据库连接等。对于线程间需要共享数据的场景,应该使用其他线程同步机制,如锁、信号量、倒计时门闩等。

总之,使用 ThreadLocal 时需要仔细考虑内存泄漏风险、线程安全性、生命周期管理和并发性能,并根据实际情况选择合适的应用场景,从而确保线程间的数据隔离和程序的正确性。


ThreadLocal的高级使用案例

ThreadLocal 的高级使用案例有以下几种:

  1. 数据库连接管理:在多线程环境下,每个线程需要独立的数据库连接来执行数据库操作。可以使用 ThreadLocal 来管理每个线程的数据库连接,从而避免线程间的数据库连接共享和竞争条件。
public class DatabaseConnectionManager {
    private static ThreadLocal connectionHolder = new ThreadLocal<>();

    public static Connection getConnection() {
        Connection connection = connectionHolder.get();
        if (connection == null) {
            // 创建数据库连接
            connection = createConnection();
            connectionHolder.set(connection);
        }
        return connection;
    }

    public static void releaseConnection() {
        Connection connection = connectionHolder.get();
        if (connection != null) {
            // 关闭数据库连接
            closeConnection(connection);
            connectionHolder.remove();
        }
    }
}

  1. 用户身份认证信息传递:在多层的应用程序中,可能需要在不同层之间传递用户身份认证信息,例如用户登录信息、权限信息等。可以使用 ThreadLocal 来在不同层之间传递这些信息,避免显式传参或全局变量的使用。
public class UserContext {
    private static ThreadLocal userHolder = new ThreadLocal<>();

    public static void setUser(User user) {
        userHolder.set(user);
    }

    public static User getUser() {
        return userHolder.get();
    }

    public static void clearUser() {
        userHolder.remove();
    }
}

  1. 请求上下文管理:在基于线程的 Web 服务器中,每个请求可能需要在多个线程间共享一些上下文信息,例如请求参数、请求头信息等。可以使用 ThreadLocal 来管理每个请求的上下文信息,从而避免线程间的上下文信息共享和竞争条件。
public class RequestContext {
    private static ThreadLocal requestHolder = new ThreadLocal<>();

    public static void setRequest(HttpServletRequest request) {
        requestHolder.set(request);
    }

    public static HttpServletRequest getRequest() {
        return requestHolder.get();
    }

    public static void clearRequest() {
        requestHolder.remove();
    }
}

  1. 线程级别的缓存:在某些场景下,需要为每个线程维护一份独立的缓存,例如线程级别的缓存,可以使用 ThreadLocal 来实现。每个线程可以在 ThreadLocal 中存储自己的缓存数据,从而避免线程间的缓存共享和竞争条件。

下面是一个使用 ThreadLocal 实现线程级别的缓存的简单案例,假设我们需要在多个线程间缓存用户信息。

public class UserCache {
    private static ThreadLocal> cacheHolder = new ThreadLocal<>();

    public static void cacheUser(User user) {
        Map cache = cacheHolder.get();
        if (cache == null) {
            cache = new HashMap<>();
            cacheHolder.set(cache);
        }
        cache.put(user.getId(), user);
    }

    public static User getUser(int userId) {
        Map cache = cacheHolder.get();
        if (cache != null) {
            return cache.get(userId);
        }
        return null;
    }

    public static void clearCache() {
        cacheHolder.remove();
    }
}

// User 类作为示例用户信息类
class User {
    private int id;
    private String name;
    // 省略其他属性和方法

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    // 省略 getter 和 setter 方法
}

在上面的例子中,UserCache​ 类使用了 ThreadLocal​ 来实现线程级别的用户信息缓存。每个线程都有自己的缓存,通过 cacheHolder​ 这个 ThreadLocal​ 对象来存储和获取缓存数据,从而避免了线程间的缓存共享和竞争条件。每个线程可以通过 UserCache​ 类的静态方法来缓存和获取用户信息,而不会影响其他线程的缓存数据。在不需要使用缓存时,可以通过 clearCache()​ 方法来清空当前线程的缓存数据,从而释放资源。

你可能感兴趣的:(java,jvm,开发语言)