Java8-ThreadLocal的Lambda构造方式:withInitial

Java8中ThreadLocal对象提供了一个Lambda构造方式,实现了非常简洁的构造方法:withInitial。这个方法采用Lambda方式传入实现了 Supplier 函数接口的参数。写法如下:

代码实例

/**
 * 当前余额
 */
private ThreadLocal<Integer> balance = ThreadLocal.withInitial(() -> 1000);

用ThreadLocal作为容器,当每个线程访问这个 balance 变量时,ThreadLocal会为每个线程提供一份变量,各个线程互不影响。

银行存款实例

附带一个银行存款的例子。

package me.zebe.cat.java.lambda;

/**
 * ThreadLocal的Lambda构造方式:withInitial
 *
 * @author Zebe
 */
public class ThreadLocalLambdaDemo {

    /**
     * 运行入口
     *
     * @param args 运行参数
     */
    public static void main(String[] args) {
        safeDeposit();
        //notSafeDeposit();
    }

    /**
     * 线程安全的存款
     */
    private static void safeDeposit() {
        SafeBank bank = new SafeBank();
        Thread thread1 = new Thread(() -> bank.deposit(200), "张成瑶");
        Thread thread2 = new Thread(() -> bank.deposit(200), "马云");
        Thread thread3 = new Thread(() -> bank.deposit(500), "马化腾");
        thread1.start();
        thread2.start();
        thread3.start();
    }

    /**
     * 非线程安全的存款
     */
    private static void notSafeDeposit() {
        NotSafeBank bank = new NotSafeBank();
        Thread thread1 = new Thread(() -> bank.deposit(200), "张成瑶");
        Thread thread2 = new Thread(() -> bank.deposit(200), "马云");
        Thread thread3 = new Thread(() -> bank.deposit(500), "马化腾");
        thread1.start();
        thread2.start();
        thread3.start();
    }

}

/**
 * 非线程安全的银行
 */
class NotSafeBank {

    /**
     * 当前余额
     */
    private int balance = 1000;

    /**
     * 存款
     *
     * @param money 存款金额
     */
    public void deposit(int money) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " -> 当前账户余额为:" + this.balance);
        this.balance += money;
        System.out.println(threadName + " -> 存入 " + money + " 后,当前账户余额为:" + this.balance);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 线程安全的银行
 */
class SafeBank {

    /**
     * 当前余额
     */
    private ThreadLocal<Integer> balance = ThreadLocal.withInitial(() -> 1000);

    /**
     * 存款
     *
     * @param money 存款金额
     */
    public void deposit(int money) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " -> 当前账户余额为:" + this.balance.get());
        this.balance.set(this.balance.get() + money);
        System.out.println(threadName + " -> 存入 " + money + " 后,当前账户余额为:" + this.balance.get());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


本文原文地址:https://blog.youkuaiyun.com/zebe1989/article/details/82692551

### Spring Boot 中 `ThreadLocal` 的用法 在 Java 编程环境中,`ThreadLocal` 提供了一种机制来创建线程局部变量。这意味着每个线程都有自己的独立实例副本,从而避免了多线程环境下的数据竞争问题[^1]。 #### 创建和初始化 ThreadLocal 变量 为了定义一个 `ThreadLocal` 实例,在类中声明它并提供初始值: ```java private static final ThreadLocal<SimpleDateFormat> dateFormatHolder = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); ``` 这段代码展示了如何利用 lambda 表达式简化构造函数调用来设置默认值。 #### 获取和设置 ThreadLocal 值 一旦有了 `ThreadLocal` 对象,就可以通过其方法获取或更新当前线程关联的数据: ```java // 设置特定于该线程的值 dateFormatHolder.set(new SimpleDateFormat("dd/MM/yyyy")); // 访问存储在线程中的对象 String formattedDate = dateFormatHolder.get().format(new Date()); System.out.println(formattedDate); ``` 当不再需要这些资源时,应该显式清除它们以防止潜在内存泄漏风险: ```java dateFormatHolder.remove(); ``` 这种做法有助于确保即使是在长时间运行的应用程序里也不会因为未释放的对象引用而导致性能下降或者 OOM 错误发生。 #### 使用场景举例 - **日期时间格式化器**: 如上所示的例子可以有效解决多个请求共享同一个 `SimpleDateFormat` 导致并发修改的问题。 - **事务管理上下文传播**: 虽然 spring-aspects.jar 包含了一个用于驱动带有 @Transactional 注解类型的 AOP 切面,但在某些情况下可能还需要借助 `ThreadLocal` 来保存额外的状态信息以便跨不同服务层传递而不会影响其他线程的行为。 #### 最佳实践建议 - 将 `ThreadLocal` 定义为静态字段,这样可以在整个应用程序生命周期内保持一致性和可预测性; - 总是记得清理不再使用的 `ThreadLocal` 数据以防泄露; - 避免过度依赖此特性处理业务逻辑,因为它可能会使代码难以理解和维护;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值