保证线程安全的方法有哪些

Java保证线程安全的常用方法(synchronized 、Atomic...) 在 Java 中,多线程并发安全是指在多个线程并发访问共享资源时,能确保程序的正确性、可靠性和一致性。为了保证多线程并发安全,Java 提供了多种方法和工具,包括关键字、类、接口等。下面我将详细讲解一些常见的方式。锁机制线程间通信Semaphore原子性操作Atomic类、volatile线程本地变量并发集合线程池Executor框架根据实际应用场景选择合适的工具和方法。对于高并发场景,尽量减少锁的竞争,使用原子操作和并发集合类,必要时才使用锁。 阅读详情

保证线程安全的方法有哪些

线程安全的核心是解决多线程同时访问共享资源时出现的原子性、可见性、有序性问题。我把常用的方法归纳为5 大类,从简单到复杂依次说明:

无状态 / 不可变对象 🏆(最优雅的方案)

  • 原理:没有共享可变状态,天然线程安全
  • 实现:
    • 使用final修饰所有字段
    • 不提供修改方法,构造函数一次性初始化
    • 典型:String、Integer等包装类、枚举
  • 优点:性能最高,无需任何同步开销
  • 缺点:灵活性差,每次修改都要创建新对象

使用线程安全类 📦(开箱即用)

JDK 提供了大量线程安全的工具类,优先使用这些成熟实现:

分类推荐使用替代的非线程安全类
集合ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySetHashMap、ArrayList、HashSet
原子类AtomicInteger、AtomicLong、AtomicReference普通基本类型 / 引用
计数器LongAdder、DoubleAdderAtomicLong(高并发下性能更好)
同步工具CountDownLatch、CyclicBarrier、Semaphore手写等待 / 通知

volatile 关键字 ✨(轻量级同步)

  • 解决的问题:可见性和有序性(禁止指令重排序)
  • 不解决的问题:原子性(比如count++这种复合操作)
  • 典型使用场景:
    • 状态标记位(boolean flag = true;)
    • 双重检查锁(DCL)单例模式
    • 发布不可变对象
// DCL单例模式的正确写法
public class Singleton {
    private static volatile Singleton instance; // 必须加volatile
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) { // 加锁
                if (instance == null) { // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

整理了面试真题、每日技术知识点、系统学习路线,√X搜「Rain的Java 大神之路」
每天拆一个知识点,陪你悄悄变强,有空来坐坐。


锁机制 🔒(最常用的同步手段)

1. synchronized 关键字
  • 特点:JVM 内置锁,自动加锁和释放锁
  • 锁的粒度:
    • 实例方法锁:锁当前对象
    • 静态方法锁:锁 Class 对象
    • 代码块锁:锁指定对象
  • 优化:JDK1.6 引入偏向锁、轻量级锁、重量级锁的升级机制
2. java.util.concurrent.locks 包下的显式锁
  • ReentrantLock:可重入锁,比 synchronized 更灵活
    • 支持公平锁 / 非公平锁
    • 可中断锁
    • 可尝试获取锁(tryLock())
  • ReentrantReadWriteLock:读写锁
    • 读 - 读不互斥,读 - 写互斥,写 - 写互斥
    • 适合读多写少的场景,大幅提升并发性能

线程封闭 🚧(隔离共享资源)

  • 原理:让数据只在一个线程内访问,从根本上避免竞争
  • 实现方式:
    • 1.栈封闭:局部变量天然线程封闭
    • 2.ThreadLocal:每个线程拥有自己的变量副本
private static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = 
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
  • 注意:ThreadLocal 使用不当会导致内存泄漏,必须在 finally 中调用remove()

各种方法的性能与适用场景对比 📊

┌─────────────────────────────────────────────────────────────────────┐
│                          代码复杂度 高 ↑                            │
│                                                                     │
│  特定场景使用                      │                      尽量避免  │
│                                    │                              │
│                                    │                              │
│  读写锁 ●                          │                              │
│                                    │                              │
│  ReentrantLock ●                   │                              │
│                                    │                              │
│  ThreadLocal ●                     │                              │
│                                    │                              │
│  原子类 ●                          │                              │
│  volatile ●                        │                              │
│  线程安全类 ●                      │                              │
│                                    │                              │
│  不可变对象 ●                      │                              │
│  无状态对象 ●                      │                              │
│────────────────────────────────────┼──────────────────────────────│
│                                    │                              │
│                                    │                              │
│                                    │                      synchronized ●
│                                    │                              │
│                                    │                              │
│                                    │                              │
│                                    │                              │
│                                    │                              │
│                                    │                              │
│                                    │                              │
│                                    │                              │
│  推荐优先使用                      │                      权衡使用  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘
                                  性能 高 →
优先级方法名称性能指数代码复杂度推荐指数
1无状态对象⭐⭐⭐⭐⭐⭐🌟🌟🌟🌟🌟
2不可变对象⭐⭐⭐⭐⭐⭐🌟🌟🌟🌟🌟
3volatile 关键字⭐⭐⭐⭐⭐⭐🌟🌟🌟🌟
4原子类 (Atomic 系列)⭐⭐⭐⭐⭐⭐🌟🌟🌟🌟
5JUC 线程安全类⭐⭐⭐⭐⭐⭐🌟🌟🌟🌟
6ThreadLocal⭐⭐⭐⭐⭐⭐🌟🌟🌟
7读写锁⭐⭐⭐⭐⭐⭐⭐🌟🌟🌟
8ReentrantLock⭐⭐⭐⭐⭐⭐⭐🌟🌟
9synchronized⭐⭐⭐⭐⭐🌟🌟

面试加分项 ✨

  1. 优先使用高层抽象:能用线程安全类就不用自己写锁
  2. 最小化锁的范围:只在必要的代码块上加锁
  3. 读写分离:读多写少场景优先使用读写锁
  4. 避免死锁:按顺序获取锁、设置超时时间、使用 tryLock
  5. 并发工具优先:优先使用 JUC 包下的工具,不要重复造轮子

总结 🎯

保证线程安全的优先级:无状态 > 不可变 > 线程安全类 > volatile > 显式锁 > synchronized。实际开发中,要根据具体场景选择最合适的方案,在性能和复杂度之间找到最佳平衡点。

核心代码与技术亮点实现 🧑‍💻

1. 不可变对象的正确实现(面试高频考点)
// 技术亮点:防止子类破坏不可变性、保证所有字段的可见性
public final class ImmutableUser { // 1. 类用final修饰,禁止继承
    private final Long id; // 2. 所有字段用final修饰
    private final String name;
    private final Date birthday; // 3. 对可变字段进行防御性拷贝

    public ImmutableUser(Long id, String name, Date birthday) {
        this.id = id;
        this.name = name;
        // 关键:不直接引用外部传入的可变对象
        this.birthday = new Date(birthday.getTime());
    }

    // 4. 不提供任何setter方法
    public Long getId() { return id; }
    public String getName() { return name; }
    
    // 5. getter返回可变对象的拷贝,而不是原引用
    public Date getBirthday() {
        return new Date(birthday.getTime());
    }
}
2. CAS 原子操作原理(AtomicInteger 源码核心)
// 技术亮点:无锁编程、CPU原语支持、乐观锁思想
public class AtomicInteger extends Number implements java.io.Serializable {
    private volatile int value; // 保证可见性
    
    public final int getAndIncrement() {
        // CAS操作:compareAndSwapInt是native方法,由CPU指令保证原子性
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    
    // Unsafe类中的核心方法
    // public final native boolean compareAndSwapInt(
    //     Object obj, long offset, int expect, int update);
}

// 手写一个简单的自旋锁(面试常考)
public class SpinLock {
    private AtomicBoolean locked = new AtomicBoolean(false);
    
    public void lock() {
        // 循环尝试获取锁,直到成功
        while (!locked.compareAndSet(false, true)) {
            // 空自旋,消耗CPU但避免线程上下文切换
        }
    }
    
    public void unlock() {
        locked.set(false);
    }
}
3. ReentrantLock 正确使用范式(避免死锁)
// 技术亮点:可中断、可超时、公平锁支持
public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
    
    public void doSomething() {
        lock.lock(); // 1. 加锁
        try {
            // 2. 业务逻辑(必须在try块内)
            System.out.println("执行业务逻辑");
        } finally {
            // 3. 必须在finally中释放锁!!!
            lock.unlock();
        }
    }
    
    // 更安全的写法:tryLock避免死锁
    public boolean tryDoSomething(long timeout, TimeUnit unit) throws InterruptedException {
        if (lock.tryLock(timeout, unit)) {
            try {
                System.out.println("获取锁成功,执行业务");
                return true;
            } finally {
                lock.unlock();
            }
        }
        // 获取锁失败
        return false;
    }
}
4. 读写锁高性能实现(读多写少场景神器)
// 技术亮点:读写分离、读-读不互斥、性能提升10倍以上
public class ReadWriteLockCache<K, V> {
    private final Map<K, V> cache = new HashMap<>();
    private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock readLock = rwLock.readLock();
    private final Lock writeLock = rwLock.writeLock();
    
    // 读操作:加读锁,多个线程可以同时读
    public V get(K key) {
        readLock.lock();
        try {
            return cache.get(key);
        } finally {
            readLock.unlock();
        }
    }
    
    // 写操作:加写锁,阻塞所有读和写
    public void put(K key, V value) {
        writeLock.lock();
        try {
            cache.put(key, value);
        } finally {
            writeLock.unlock();
        }
    }
    
    // 缓存更新:先写后读的原子操作
    public V putIfAbsent(K key, V value) {
        writeLock.lock();
        try {
            if (!cache.containsKey(key)) {
                cache.put(key, value);
                return null;
            }
            return cache.get(key);
        } finally {
            writeLock.unlock();
        }
    }
}
5. ThreadLocal 正确使用(避免内存泄漏)
// 技术亮点:线程封闭、避免参数传递、SimpleDateFormat线程安全问题解决
public class ThreadLocalExample {
    // 正确写法:使用static final修饰ThreadLocal
    private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT =
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    
    public String formatDate(Date date) {
        try {
            return DATE_FORMAT.get().format(date);
        } finally {
            // 关键:使用完必须remove!!!防止内存泄漏
            DATE_FORMAT.remove();
        }
    }
}

核心技术难点与解决方案汇总 🚨

技术难点问题表现根本原因最佳解决方案
死锁问题 💀程序卡死,CPU 使用率低,线程 BLOCKED 状态多个线程循环等待对方持有的锁1. 按固定顺序获取锁2. 使用tryLock()设置超时3. 避免一个线程同时持有多个锁4. 使用jstack命令排查死锁
可见性问题 👁️一个线程修改了变量,另一个线程看不到CPU 缓存与主内存不一致1. 使用volatile关键字2. 使用 synchronized 或 Lock3. 使用原子类
指令重排序问题 🔄DCL 单例模式返回 null、对象半初始化JVM 为了优化性能对指令进行重排序1. 使用volatile禁止指令重排序2. 使用静态内部类实现单例
原子性问题 ⚛️count++结果不正确、超卖问题复合操作不是原子性的1. 使用原子类(AtomicInteger)2. 使用 synchronized 或 Lock3. 使用分段锁(LongAdder)
ThreadLocal 内存泄漏 🧠OOM 异常、内存占用持续升高ThreadLocalMap 的 Entry 是弱引用,Value 是强引用1. 使用static final修饰 ThreadLocal2. 必须在 finally 中调用remove()3. 避免存储大对象
锁性能问题 🐌并发量上不去、吞吐量低锁竞争激烈、锁粒度过大1. 缩小锁的范围(只锁必要代码)2. 使用读写锁(读多写少)3. 使用分段锁(ConcurrentHashMap)4. 使用无锁编程(CAS)
并发集合陷阱 🪤ConcurrentHashMap的 putIfAbsent 误用复合操作不保证原子性1. 使用原子方法(putIfAbsent、compute)2. 不要先检查再操作3. 避免在遍历中修改集合
伪共享问题 🚫多线程操作数组性能极差多个变量位于同一个 CPU 缓存行1. 使用缓存行填充(@Contended 注解)2. 避免多个线程频繁操作相邻变量

面试加分亮点代码 🎖️

静态内部类单例模式(完美解决 DCL 问题)
// 技术亮点:懒加载、线程安全、无锁、性能最高
public class Singleton {
    private Singleton() {} // 私有构造函数
    
    // 静态内部类,只有在第一次被调用时才会加载
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}
LongAdder 分段锁思想(高并发计数器首选)
// 技术亮点:分段锁、减少CAS竞争、高并发下性能比AtomicLong高10倍
public class LongAdder extends Striped64 implements Serializable {
    // 核心思想:将一个long值拆分成多个Cell
    // 不同线程更新不同的Cell,最后求和
    // 只有当Cell竞争失败时才会扩容
    
    public void add(long x) {
        Cell[] as; long b, v; int m; Cell a;
        if ((as = cells) != null || !casBase(b = base, b + x)) {
            boolean uncontended = true;
            if (as == null || (m = as.length - 1) < 0 ||
                (a = as[getProbe() & m]) == null ||
                !(uncontended = a.cas(v = a.value, v + x)))
                longAccumulate(x, null, uncontended);
        }
    }
    
    public long sum() {
        Cell[] as = cells;
        long sum = base;
        if (as != null) {
            for (Cell a : as)
                if (a != null)
                    sum += a.value;
        }
        return sum;
    }
}

总结 🎯

线程安全的核心是正确处理原子性、可见性、有序性三个问题。在实际开发中,我们应该遵循 “能不用锁就不用锁,能用高层抽象就不用底层实现” 的原则,优先选择无状态、不可变对象和 JUC 包提供的成熟工具类,只有在必要时才使用锁机制。

真实面试模拟

真实面试模拟

面试官 👨‍💼:

行,先来个基础题暖暖场。保证线程安全的方法你能想到哪些?随便说,不要求全。

候选人 🧑‍💻:

好的面试官。我先说下我的理解:线程不安全本质上是多个线程同时写同一个可变资源导致的竞态条件。

解决方案我习惯分成三大类:

  1. 互斥同步(悲观锁):同一时间只让一个线程操作,比如 synchronized、ReentrantLock。
  2. 非阻塞同步(乐观锁):基于 CAS 的原子类,如 AtomicInteger。
  3. 无同步方案:干脆不共享,比如不可变对象、ThreadLocal、栈封闭。

这三类就像处理多人抢卫生间:第一类是排队加锁,第二类是大家先上,冲突再协调,第三类是给每人发个独立卫生间 😄。

面试官 👨‍💼:

哈哈,这个比喻有意思。那我们先聊第一类。synchronized 你用过吧?它和 ReentrantLock 有什么本质区别?什么时候你会选后者?

候选人 🧑‍💻:

用过,我项目里支付回调的幂等控制就用了 synchronized。

区别的话:

  • synchronized 是 JVM 内置锁,自动释放,代码简单,但是功能上比较“憨”,没法中途放弃。
  • ReentrantLock 是 API 层面的锁,灵活:可以 tryLock 带超时、可中断、可设公平锁,还能绑定多个 Condition 做精准唤醒。

选后者的场景:

比如调用一个外部接口,我不希望线程傻等,就用 tryLock(200, TimeUnit.MILLISECONDS),拿不到锁直接降级。这种场合 synchronized 就做不到。

面试官 👨‍💼:

很好,抓住了灵活性的关键点。那 读写锁你了解吗?什么情况下它比独占锁性能好?

候选人 🧑‍💻:

读写锁就是 ReentrantReadWriteLock,它把锁分成读锁(共享锁)和写锁(独占锁)。读和读不互斥,读和写互斥。

典型场景:配置中心,读配置的线程非常多,写配置很少。如果用 synchronized,所有读都互斥,吞吐量上不去。换成读写锁后,大部分读操作可以并发,只有写时才阻塞所有人。当然,用时要小心锁降级的规则。

面试官 👨‍💼:

不错。那咱们来看第二类——非阻塞同步。CAS 是什么原理?原子类为什么能无锁却能线程安全?

候选人 🧑‍💻:

CAS 就是 Compare-And-Swap,是 CPU 级别的原子指令。好比你去改一个值,改之前先核对一下手里的旧值是不是跟主存里一致,一致就换上新值,不一致就说明别人改过,重新读再试。

Java 里的 AtomicInteger.incrementAndGet() 内部就是循环 CAS,直到成功,不依赖操作系统挂起线程,所以高并发下吞吐量更高,没有上下文切换开销。不过它有 ABA 问题,可以用 AtomicStampedReference 加版本号解决。

面试官 👨‍💼:

接着你来谈谈无同步方案吧,怎么才能做到不共享?

候选人 🧑‍💻:

核心思想是“彻底消灭共享资源”,有三个典型手段:

  1. 不可变对象
    比如 String,一经创建状态不可改。自己写类用 final 修饰 class,所有字段 private final,不提供 setter。这种对象天生线程安全,随便多线程读。
  2. ThreadLocal
    给每个线程一个独立的数据副本,线程之间物理隔离。像 Spring 的 RequestContextHolder 就用 ThreadLocal 保存当前请求。用完后必须 remove(),否则在线程池场景下会内存泄漏。
  3. 栈封闭
    方法里的局部变量存在每个线程自己栈里,绝无共享,自然安全。只要别把局部变量的引用发布出去就行。

另外,无状态设计也算:一个 Service 没有成员变量,所有逻辑都在方法参数和局部变量上,多线程调用天然安全。

面试官 👨‍💼:

思路很清晰。那你能不能快速给我画个全景图,把今天聊的这些串起来?

候选人 🧑‍💻:

没问题,我文字描述下,您可以想象一个脑图 😄

🎯 保证线程安全
├─ 🔒 互斥同步(悲观锁)
│   ├─ synchronized(内置锁,自动)
│   ├─ ReentrantLock(显式锁,灵活)
│   └─ ReadWriteLock/StampedLock(读多写少)
├─ ⚡ 非阻塞同步(乐观锁)
│   └─ CAS 原子类(AtomicInteger, AtomicReference)
└─ 🚫 无同步方案(避免共享)
    ├─ 不可变对象(String,Integer)
    ├─ ThreadLocal(线程私有副本)
    ├─ 栈封闭(局部变量)
    └─ 无状态设计(无成员变量)
面试官 👨‍💼:

最后考你一下场景:如果让你给一个全局的统计接口调用次数设计线程安全的计数器,读写比大概 100:1,你选哪个方法?

候选人 🧑‍💻:

读写比极高,明显读远大于写。

方案一:直接用 AtomicLong,读非常快(无锁),写也是 CAS 轻量自旋,足够。

但如果读的是很复杂的衍生数据,我可能会用 ReentrantReadWriteLock,让大量读线程完全并发。不过对于简单计数器,AtomicLong 更简洁,实测性能也极好。我再补一句,JDK8 还提供了 LongAdder,高并发写时有更好的分散热点设计。

面试官 👨‍💼:

我看你理论掌握得很扎实,接下来咱们走点实战的。你刚才提到了几种同步方式,能不能现场写几段核心代码,把最有技术亮点的用法展示出来?顺便说说为什么这么写有亮点。

候选人 🧑‍💻:

好的,那我写三段代码,分别对应互斥同步的高级用法、非阻塞同步的精髓以及无同步方案里最容易踩坑的点。

1️⃣ ReentrantLock 的 tryLock 超时与降级处理

public class TimeoutRetryService {
    private final Lock lock = new ReentrantLock();

    public boolean processWithTimeout(String orderId) {
        boolean acquired = false;
        try {
            // 🌟 技术亮点:尝试获取锁,等200ms拿不到就降级,不阻塞
            acquired = lock.tryLock(200, TimeUnit.MILLISECONDS);
            if (!acquired) {
                // 降级策略:放入延迟队列,或返回“操作进行中”
                System.out.println("系统繁忙,订单 " + orderId + " 稍后重试");
                return false;
            }
            // 临界区:真正处理业务
            doActualWork(orderId);
            return true;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false;
        } finally {
            // 🌟 亮点:防止意外释放未持有的锁
            if (acquired) {
                lock.unlock();
            }
        }
    }

    private void doActualWork(String orderId) {
        // 模拟幂等操作
    }
}
亮点说明:
  • tryLock 带超时 + 降级,避免线程饿死在锁上。
  • finally 中通过 acquired 标志判断是否成功获取,防止 IllegalMonitorStateException,这是工程里很常见的健壮性写法。

2️⃣ AtomicInteger 的 CAS 循环 —— 高性能计数器

public class CasCounter {
    private final AtomicInteger count = new AtomicInteger(0);

    public int incrementAndGet() {
        int prev, next;
        do {
            prev = count.get();
            next = prev + 1;
            // 🌟 亮点:CAS自旋,无锁,高并发下吞吐量远超synchronized
        } while (!count.compareAndSet(prev, next));
        return next;
    }

    // JDK8 提供的更简洁写法,内部就是 CAS
    public int quickIncrement() {
        return count.incrementAndGet(); // 一行搞定
    }
}
亮点说明:
  • 手动展示了 CAS 循环的原始逻辑,让面试官知道你不只会用 API,还懂原理。
  • 对比 incrementAndGet(),说明底层也是 CAS,上层才是原子包装。

3️⃣ ThreadLocal 的正确使用与内存泄漏预防

public class RequestContextHolder {
    private static final ThreadLocal<Map<String, Object>> CONTEXT = 
        ThreadLocal.withInitial(HashMap::new);

    public static void set(String key, Object value) {
        CONTEXT.get().put(key, value);
    }

    public static Object get(String key) {
        return CONTEXT.get().get(key);
    }

    // 🌟 核心亮点:显式提供清理方法,防止线程池复用导致内存泄漏
    public static void clear() {
        CONTEXT.remove(); // 必须调!
    }
}

// 使用方:在过滤器 finally 块中清理
try {
    RequestContextHolder.set("userId", "10086");
    // ... 业务处理
} finally {
    RequestContextHolder.clear(); // ⚠️ 关键一步
}
亮点说明:
  • ThreadLocal.withInitial() 提供初始值,代码更优雅。
  • 重点强调了 remove(),这是生产环境中内存泄漏的高发点,能提到它说明你有 JVM 调优意识。
面试官 👨‍💼:

不错,三段代码都把“坑”和“亮点”点到了。最后,咱们总结一下,在保证线程安全的设计中,你遇到过哪些技术难点,分别怎么解决?

候选人 🧑‍💻:

我总结五个最常见的难点和方案:

技术难点典型场景解决方案
1. 复合操作的原子性if(!map.containsKey(key)) map.put(...) 这样的“检查-执行”操作用 ConcurrentHashMap.putIfAbsent(),或把操作包在 synchronized 块中,但优先用并发容器的原子方法
2. 锁粒度控制与死锁大锁导致性能差,多锁嵌套导致死锁缩小同步范围(快进快出),统一加锁顺序,用 tryLock 超时预防死锁,或用 jstack 检测
3. CAS 的 ABA 问题链表头节点从 A→B→A,虽然值相同但状态已变使用带版本号的 AtomicStampedReference 或 AtomicMarkableReference,在链表操作中尤其关键
4. ThreadLocal 内存泄漏线程池线程复用,ThreadLocal 引用不清理导致 OOM强制在 finally 中调用 remove(),或者使用带弱引用的自定义 ThreadLocal,建议所有框架都内置清理
5. 可见性 & 指令重排一个线程改了值,另一个线程永远看不见用 volatile 保证可见性和禁止指令重排,或者使用锁(隐式内存屏障),避免依赖“运气”
面试官 👨‍💼:

很好,理论+代码+难点全闭环了,这个问题你通过得很漂亮。咱们进入下一题 🚀

【Flink】Flink 窗口操作的 processElement 和 onTimer 方法是如何保证线程安全的 需要注意的是,如果 processElement 方法中更新了状态(例如修改状态中的某个值),而该状态在 onTimer 方法中被引用,那么 onTimer 方法中引用的状态将会是更新后的状态。具体怎么实现 当 onTimer 方法正在处理时,如果 processElement 方法中有新的数据进入,那么这些新数据的处理需要等待当前的 onTimer 方法完成后才能被处理。处理processElement 方法中的数据不会直接影响正在进行的 onTimer 方法的状态或数据。 阅读详情

相关推荐

开发板的使用

一、开发板使用学习大纲。 1、开发板组成、核心板资源,底板资源。 2、开发板连接、调试工具、串口参数配置。 3、开发板启动过程。 4、文件传输 1)使用串口传输。 上传/下载 2)使用网口传输。 上传/下载 5、临时/永久配置开发板IP地址。 6、开发板启动脚本。 二、开发板的组成。 核心板: CPU处理器: 芯片:S5P6818(三星) 内核: ARM cortex-A53 -> 八核处理器 运行内存:512MB*2=1G nandflash:4G 底板: 电源线:5V 串口...

qq_45633736的博客 2658

保证线程安全的四种方法

保证线程安全的四种方法 1.Confinement 限制数据共享。 将可变数据限制在单一线程内部,避免竞争。核心思想就是线程之间不共享可变数据类型。 2.Immutable 将可变数据类型改为Immutable类型。 避免多线程间的race condition。 3.Threadsafe data type 共享线程安全的可变数据。 如果必须要在多线程间使用mutable的数据类型,必须要使用线程安全的数据类型。在JDK的类文档中,记录着是否线程安全。如List,Set,Map等集合类,都是线程不安全的。

weixin_45640265的博客 3204

MATLAB 设置legend加标题

x1 = linspace(0,5); y1 = sin(x1/2); plot(x1,y1) hold on x2 = [0 1 2 3 4 5]; y2 = [0.2 0.3 0.6 1 0.7 0.6]; scatter(x2,y2,'filled') hold off legend({'sin(x/2)','2016'},'Location','northwest','Orientation','horizontal')%水平 legend添加标题 lgd = legend; lgd.Fon

早起CaiCai的学习空间 4713

什么是线程安全?如何保证线程安全?Java中保证线程安全的方法有哪些?【重要】...

一、什么是线程安全? 简单来说,线程安全是多个线程访问同一段代码,不会造成不确定的结果。 ...

m0_51697147的博客 313

⑩电子产品拆解分析-家用无线遥控开关433Mhz

①免布线随意贴,装上就能使用解决单线开关烦恼;②遥控配对简单,无线通讯距离长,信号可穿墙;

a6662580的博客 4681

#systemverilog# 之 event region 和 timeslot 仿真调度(二)例子

由于疫情的关系,一时半会回不了学校了。今天开始,哪怕在家也要克服惰性开始学习了。今天来个的想了很久但都犯懒而没动手的内容,就是关于仿真软件在跑systemverilog的时候,是如果通过将HDL代码分成5个区域来编译,从而实现用软件模拟硬件的运行结果的思想。 首先,是基础概念,systemverilog标准划分了仿真软件的5个跑仿真所要执行的区域,说白了就是规定了软件在跑代码的时候要把代码分成5类,放到5个堆栈里面分别跑,这样才能:明明在软件上是以顺序方式来执行代码的,却能“看似”像在硬件中那样的实现并行

那么菜的博客 1762

保证线程安全的方法

要,关键在于防止多个线程同时访问共享资源时产生竞态条件(race condition)。

qq_45512728的博客 523

2024美团秋招硬件开发笔试真题及答案解析

在 Linux 内核的网络设备驱动中,数据链路层的功能通常由网络协议栈的低层(如以太网驱动)实现,而不是作为网络设备驱动的一个独立层次。在 FreeRTOS 中,任务通知(Task Notifications)是一种轻量级的事件传递机制,用于在任务之间或任务与中断之间传递信号。小美正在开发一个简单的密码生成器,其中一个功能是将输入的字符串中的所有字母按字母顺序循环前移一位(即 'a' 变为 'z','B' 变为 'A',以此类推)。它包含了网络设备的各种参数和方法,用于管理网络设备的初始化、配置和操作。

XU157303764的博客 1674

java 保证线程安全_(原创)确保JAVA线程安全的4种常用方法

在Java中可以有很多方法来保证线程安全,比如使用同步方法、同步块,使用原子类(atomic concurrent classes),实现并发锁,使用volatile关键字,使用不变类和线程安全类。这里是最基础的线程安全教程实际上在volatile的使用上很容易有误解,以为volatile就可以做原子操作,实际不然。Volatile 变量具有synchronized的可见性特性,但是不具备原子...

weixin_26750831的博客 809

外设测试 - ATAES132A 加解密芯片测试

原理 使用芯片官方提供的读写操作库,进行先写后读并校验,确认数据一致。 示例代码 int main(void) { uint8_t ret; aes132p_enable_interface(); ret = write_memory_test(); if (ret != AES132_FUNCTION_RETCODE_SUCCESS) { aes132p_disable_interface(); printf

一个软件工程师的技术笔记 853

RK3568移植AP6256

本文主要是记录RK3568移植正基的AP6256的WIFIBT模块,感兴趣的朋友可以一起学习!!!提示:以下是本篇文章正文内容,下面案例可供参考。

qq_24093081的博客 3715

在Java如何保证方法是线程安全的

在Java如何保证方法是线程安全的   废话开篇   都说Java程序好些,但是我觉得Java编程这东西,没个十年八年的真不敢说自己精通Java编程,由于工作原因,开始转战Java多线程,以前没怎么接触过,所以想留点脚印在这两条路上。   切入正题   开门见山,今天看到别人写的一段关于方法是否线程安全的文章,比较简单,但是由于自己也是刚开始入门,所以就迈下了第一步。由于注释还算比较详细,

aedggd的专栏 711

华为od 面试八股文_前端_02_含答案

华为od 面试八股文_前端_02_含答案

MISAYAONE的博客 1996

获取单例对象需要保证线程安全,其中的方法也要保证线程安全

获取单例对象需要保证线程安全,其中的方法也要保证线程安全。说明:资源驱动类、工具类、单例工厂类都需要注意。 转载于:https://www.cnblogs.com/amos-s/p/6387482.html...

weixin_33694172的博客 1470

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

不是线程安全的数据结构以及保证线程安全的方法

不是线程安全的数据结构:HashMap,ArrayList, LinkedList 保证线程安全的方法:加锁,比如synchronize/ReentrantLock,使用volatile声明变量,轻量级同步,不能保证原子性,使用线程安全类(原子类AtomicXXX,并发容器,同步容器 CopyOnWriteArrayList/ConcurrentHashMap等,ThreadLocal本地私有变量/信号量Semaphore等 ...

AaronHyk的博客 410

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Stream的collect方法是怎么保证线程安全的

写这篇文章是因为前段时间有个同事用parallelStream处理集合的时候发现数据丢了。因为是线上问题所以和他一起看了一下。 出问题的代码大致如下: List<Foobar> result = new ArrayList<>(); fooList.parallelStream().forEach(bar -> { Foobar foobar = new...

z69183787的专栏 4386

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Java中保证线程安全的方法总结

使用synchronized关键字创建一个同步代码块,确保一次只有一个线程可以执行该代码块。这可以通过锁定一个对象来实现,任何想要进入该代码块的线程都必须先获取这个对象的锁。

m0_68662280的博客 727

Java之HashMap系列--保证线程安全的方法

本文介绍Java中的HashMap如何进行线程安全的操作、为什么HashMap不是线程安全的。 这几个问题也是Java后端面试中经常问到的问题。

IT利刃出鞘的博客 9129

JAVA虚拟机有义务保证<clinit>()方法的线程安全

在Java中,JVM负责确保类初始化方法<clinit>()的线程安全性。通过一个示例代码展示了这一点,其中两个线程尝试同时初始化一个包含静态代码块的类ClinitObject。静态代码块中包含一个死循环,依赖于一个静态的AtomicBoolean变量flag。由于JVM的机制,只有一个线程能够执行<clinit>()方法,其他线程会被阻塞直到初始化完成。这保证了类初始化过程的线程安全,避免了多线程环境下的竞争条件。示例中,第一个线程进入静态代码块并陷入死循环,第二个线程被阻塞,直到

weixin_46102873的博客 333

保证线程安全方法

线程安全是编程过程中很容易被忽略的问题,而一旦出现线程安全问题,会导致程序dubug过程变得异常艰难,这里总结了几种保证线程安全的方法: 1、拒绝多个线程访问同一个变量,也就是将多线程任务变成单线程,但这会大大降低程序的运行效率,这显然是不可取的。 2、使用immutable变量,immutable变量由于其不可修改的特性,他们往往都是线程安全的,这也就是要求各线程仅能读变量而不能写。但这种约束也往往是不实际的,很少有程序不会修改变量的值。 3、使用java提供的线程安全的类,java提供了一些线程安全的类

yutg5的博客 741

如何保证线程安全有序性_线程安全和保证线程安全的两个方法

之前讲了什么是线程和多线程,接下来敲黑板:线程安全。什么是线程安全?线程安全就是确保程序在多线程运行的情况下,程序能够按照预期运行,不会存在二义性的结果。比如上一篇我们提到的火车票的售票问题,预期的情况下,我们需要确保同一张票只能被唯一的窗口售出一次。下面我们用代码说明:(我们这里调用Runnable实现多线程,不保证线程安全的情况)Class1:实现一个Runnable接口public clas...

weixin_36079903的博客 298

解决(保证)线程安全问题的三种方法

线程同步 当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。 要解决上述多线程并发访问一一个资源的安全性问题:也就是解决重复票与不存在票问题, Java中提供了同步机制 (synchronized)来解决。 根据案例简述: 窗口1线程进入操作的时候,窗口2和窗口3线程只能在外等着,窗口1操作结束,窗口1和窗口2和窗口3才有机会进入代码 去执行。也就是说在某个线程修改共享资源的时候,其他线程不能修改该资源,等待修改完毕同步之后,才能去抢夺CPU 资源,完成对应的..

weixin_51980491的博客 1796

如何保证线程安全 实现线程安全有那些方法

1,如何保证线程安全 不在线程之间共享状态变量 将状态变量修改成不可变的变量 在访问状态变量时使用同步   2,实现线程安全有那些方法 A:内置锁 public class widget{ public synchronized void doSomething(){ ..... } } public class Loggi...

sdywcd——jshoper3x开源商城系统作者 639
上一篇: DTO、VO、PO 到底要不要拆?一篇讲透 Java 实体分层的底层逻辑
下一篇: 100亿订单号如何去重
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值