基于 “不可变类” 实现一个线程安全的 Integer 计数器

本文介绍如何通过创建不可变类ImmutableIntegerCounter实现线程安全的计数器。不可变类的特性确保在多线程环境下,每次操作都返回新的对象,避免了同步和锁带来的性能问题,确保数据一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

写在前面

众所周知,java.lang.String 类没有任何强制同步的代码,但它是线程安全的,原因在于它是不可变类,每次对其操作都是返回一个新的 String 对象。合理使用不可变对象可以达到 lock-free 的效果。

不可变对象最核心的地方在于不给外部修改共享资源的机会,从而避免多线程情况下由于争抢共享资源导致的数据不一致,又是 lock-free 的,避免了用锁带来的性能损耗。

ImmutableIntegerCounter

// final 修饰,不能继承
public final class ImmutableIntegerCounter {

    // final 修饰,不允许其他线程对其更改
    private final int initial;

    public ImmutableIntegerCounter(int initial) {
        this.initial = initial;
    }

    public ImmutableIntegerCounter(ImmutableIntegerCounter counter, int i) {
        this.initial = counter.getInitial() + i;
    }

    // 每次调用 increment 都会新构造一个 ImmutableIntegerCounter
    public ImmutableIntegerCounter increment(int i) {
        return new ImmutableIntegerCounter(this, i);
    }

    public int getInitial() {
        return initial;
    }
}

测试

import org.junit.Before;
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class ImmutableIntegerCounterTest {

    private ImmutableIntegerCounter integerCounter = null;

    @Before
    public void init() {
        integerCounter = new ImmutableIntegerCounter(0);
    }

    @Test
    public void incrementTest() throws InterruptedException {

        IntStream.range(0, 3).forEach(i -> new Thread(() -> {
            int increment = 1;
            while(true) {
                int oldValue = integerCounter.getInitial();
                int result = integerCounter.increment(increment).getInitial();
                System.out.println(oldValue + "+" + increment + "=" + result);
                assert (oldValue + increment) == result;
                increment++;
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start());

        TimeUnit.SECONDS.sleep(10);

    }
}

输出结果

三个线程的输出结果均是正确的

0+1=1
0+1=1
0+1=1
0+2=2
0+2=2
0+2=2
0+3=3
0+3=3
0+3=3
0+4=4
0+4=4
0+4=4

Process finished with exit code 130 (interrupted by signal 2: SIGINT)
<think>我们正在讨论实现线程安全的方法和技术。根据引用内容,我们可以总结出以下几种实现线程安全的方式: 1. 使用同步代码块(synchronized block)或同步方法(synchronized method) 2. 使用锁(Lock)机制,如ReentrantLock 3. 使用原子变量(Atomic classes) 4. 使用线程安全的集合类(如ConcurrentHashMap, CopyOnWriteArrayList等) 5. 使用不可变对象(Immutable objects) 6. 使用线程局部变量(ThreadLocal) 下面我们将详细展开这些方法,并给出相应的代码示例(以Java为例)。</think>### 实现线程安全的方法与技术详解 线程安全是指多线程环境下,共享数据的访问和修改不会导致数据不一致或程序行为异常。以下是实现线程安全的核心方法和技术: --- #### **1. 互斥同步(悲观锁)** - **synchronized 关键字** 通过对象监视器(Monitor)实现互斥访问: ```java // 同步方法 public synchronized void increment() { count++; } // 同步代码块 public void add() { synchronized(this) { // 锁对象 value += 10; } } ``` 特点:自动加锁/解锁,支持重入,但性能开销较大[^1][^3]。 - **ReentrantLock 显式锁** 更灵活的锁控制: ```java private final ReentrantLock lock = new ReentrantLock(); public void update() { lock.lock(); // 手动加锁 try { // 临界区操作 } finally { lock.unlock(); // 必须手动释放 } } ``` 优势:支持公平锁、可中断锁、超时锁等高级特性[^1][^3]。 --- #### **2. 非阻塞同步(乐观锁)** - **原子类(Atomic Classes)** 基于 CAS(Compare-And-Swap)实现无锁线程安全: ```java private AtomicInteger counter = new AtomicInteger(0); public void safeIncrement() { counter.getAndIncrement(); // CAS原子操作 } ``` 适用场景:计数器、状态标志等简单操作[^1][^4]。 - **Volatile 变量** 保证可见性(不保证原子性): ```java private volatile boolean flag = false; // 多线程即时可见 ``` 限制:仅适用于单写多读场景,复合操作仍需同步[^3]。 --- #### **3. 线程安全容器** | 类型 | 非线程安全 | 线程安全替代方案 | |---------------------|-------------------|-----------------------------| | **List** | ArrayList | `CopyOnWriteArrayList` | | **Map** | HashMap | `ConcurrentHashMap` | | **Set** | HashSet | `ConcurrentSkipListSet` | | **Queue** | LinkedList | `BlockingQueue`实现类 | - **ConcurrentHashMap 原理**: 分段锁(JDK7)或 CAS + synchronized(JDK8+),并发度更高[^2]。 --- #### **4. 不可变对象(Immutable Objects)** - 通过 final 字段和构造器初始化实现: ```java public final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } // 无setter方法 } ``` 优势:天然线程安全,无需同步[^1]。 --- #### **5. 线程封闭** - **ThreadLocal 局部变量** 每个线程独享数据副本: ```java private static ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); ``` 典型应用:数据库连接、Session管理[^3]。 - **栈封闭** 局部变量(存在于线程栈)天然线程安全: ```java public void process() { int localVar = 0; // 不会被其他线程访问 localVar++; } ``` --- #### **6. 避免共享状态的设计模式** - **无状态对象** 不包含任何字段的类(如工具类): ```java public class MathUtils { public static int add(int a, int b) { return a + b; // 无成员变量 } } ``` - **消息传递(Actor模型)** 线程间通过消息队列通信(如Akka框架)。 --- #### **线程安全实践原则** 1. **优先使用现有线程安全类**(如 `ConcurrentHashMap`)[^2] 2. **缩小同步范围**(同步块 > 同步方法) 3. **避免锁嵌套**(预防死锁) 4. **同步与 volatile 互补使用** 5. **测试验证**(压力测试、静态分析工具) > 示例:使用 `ConcurrentHashMap` 避免脏数据[^4] > ```java > Map<String, Integer> safeMap = new ConcurrentHashMap<>(); > safeMap.compute("key", (k, v) -> (v == null) ? 1 : v + 1); // 原子更新 > ``` --- ```mermaid graph LR A[线程安全实现方案] --> B[互斥同步] A --> C[非阻塞同步] A --> D[线程安全容器] A --> E[不可变对象] A --> F[线程封闭] B --> B1(synchronized) B --> B2(ReentrantLock) C --> C1(原子类) C --> C2(Volatile) D --> D1(ConcurrentHashMap) D --> D2(CopyOnWriteArrayList) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值