线程安全有哪些实现思路?

简介: 线程安全有哪些实现思路?

1.互斥同步

synchronized 和 ReentrantLock。

2.非阻塞同步

互斥同步最主要的问题就是线程阻塞和唤醒所带来的性能问题,因此这种同步也称为阻塞同步。

互斥同步属于一种悲观的并发策略,总是认为只要不去做正确的同步措施,那就肯定会出现问题。无论共享数据是否真的会出现竞争,它都要进行加锁(这里讨论的是概念模型,实际上虚拟机会优化掉很大一部分不必要的加锁)、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要唤醒等操作。

  • CAS

随着硬件指令集的发展,我们可以使用基于冲突检测的乐观并发策略: 先进行操作,如果没有其它线程争用共享数据,那操作就成功了,否则采取补偿措施(不断地重试,直到成功为止)。这种乐观的并发策略的许多实现都不需要将线程阻塞,因此这种同步操作称为非阻塞同步。

乐观锁需要操作和冲突检测这两个步骤具备原子性,这里就不能再使用互斥同步来保证了,只能靠硬件来完成。硬件支持的原子性操作最典型的是: 比较并交换(Compare-and-Swap,CAS)。CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。

  • AtomicInteger

J.U.C 包里面的整数原子类 AtomicInteger,其中的 compareAndSet() 和 getAndIncrement() 等方法都使用了 Unsafe 类的 CAS 操作。

AtomicInteger是Java中的一个原子操作类,它位于java.util.concurrent.atomic包下。AtomicInteger提供了一种线程安全的方式来操作int值,它可以在多线程环境下保证对int值的原子性操作。

AtomicInteger的主要作用是提供原子操作来进行Integer的使用,适合高并发情况下的使用。在多线程环境下,当有多个线程同时执行AtomicInteger实例包含的方法时,具有排它性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断。

AtomicInteger类是系统底层保护的int类型,通过对int类型的数据进行封装,提供执行方法的控制进行值的原子操作。它提供了一些方法来进行原子操作,如incrementAndGet()、getAndIncrement()、getAndAdd()等。这些方法可以保证在多线程环境下对int值的原子性操作,避免了使用同步锁等机制带来的性能开销。

总的来说,AtomicInteger是一种高效的线程安全操作类,适用于需要高并发操作对int值进行原子性操作的场景。

3.无同步方案

要保证线程安全,并不是一定就要进行同步。如果一个方法本来就不涉及共享数据,那它自然就无须任何同步措施去保证正确性。

  • 栈封闭

多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在虚拟机栈中,属于线程私有的。

"栈封闭"这个概念在计算机科学中通常是指一个栈(数据结构)在某一时刻达到了稳定状态,即在该时刻之后,栈中的元素不会因为新的元素入栈而发生改变。

更具体地说,如果一个栈在某一时刻之后不再有新的元素入栈,那么这个栈就被称为在该时刻是封闭的。封闭栈的顶部元素(最后入栈的元素)总是可用的,因为它没有被其他元素覆盖。

栈封闭的概念通常在计算机科学和编程中使用,特别是在处理递归和深度优先搜索(DFS)等问题时。例如,在解析复杂的语法结构(如编程语言的解析)时,可以使用栈来存储解析过程中的状态,如果在这个过程中栈始终保持封闭状态,那么就可以避免出现错误。

请注意,这个概念并不是数学意义上的封闭,而是指一种特定的计算机科学现象。

  • 线程本地存储(Thread Local Storage)

如果一段代码中所需要的数据必须与其他代码共享,那就看看这些共享数据的代码是否能保证在同一个线程中执行。如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。

线程本地存储(Thread-local Storage,TLS)是一种机制,它为每个线程提供独立的存储空间。这样,每个线程都可以在其自己的存储空间中存储数据,而不会与其他线程的数据冲突。这可以避免并发问题,使得每个线程都可以安全地访问其自己的数据。

在实现上,线程本地存储通常是通过使用一个映射表来实现的,表中每个线程ID映射到一个特定的数据块。当线程需要访问其本地数据时,它首先需要查找映射表,找到对应的数据块,然后在这个数据块中进行操作。

这种机制对于需要为每个线程提供独立数据的情况非常有用,例如在多线程编程中,当多个线程需要访问共享资源(例如数据库)时,使用线程本地存储可以避免数据冲突和并发问题。

相关文章
|
10月前
|
存储 缓存 安全
面试官:说说volatile底层实现原理?
面试官:说说volatile底层实现原理?
515 5
面试官:说说volatile底层实现原理?
|
6月前
|
缓存 Java 编译器
JAVA并发编程volatile核心原理
volatile是轻量级的并发解决方案,volatile修饰的变量,在多线程并发读写场景下,可以保证变量的可见性和有序性,具体是如何实现可见性和有序性。以及volatile缺点是什么?
|
5月前
|
存储 安全 Java
面试题:再谈Synchronized实现原理!
面试题:再谈Synchronized实现原理!
|
7月前
|
安全 Java
【Java集合类面试十三】、HashMap如何实现线程安全?
实现HashMap线程安全的方法包括使用Hashtable类、ConcurrentHashMap,或通过Collections工具类将HashMap包装成线程安全的Map。
|
7月前
|
Java 调度
【多线程面试题十四】、说一说synchronized的底层实现原理
这篇文章解释了Java中的`synchronized`关键字的底层实现原理,包括它在代码块和方法同步中的实现方式,以及通过`monitorenter`和`monitorexit`指令以及`ACC_SYNCHRONIZED`访问标志来控制线程同步和锁的获取与释放。
|
安全
多线程访问同步方法的7种情况(面试常考)
多线程访问同步方法的7种情况(面试常考)
80 0
多线程访问同步方法的7种情况(面试常考)
|
10月前
|
缓存 安全 Java
多线程--深入探究多线程的重点,难点以及常考点线程安全问题
多线程--深入探究多线程的重点,难点以及常考点线程安全问题
197 1
|
10月前
|
缓存 安全 Java
Java并发编程中的线程安全问题及解决方法
在Java编程中,线程安全是一个至关重要的问题,特别是在并发编程中。本文将探讨Java并发编程中常见的线程安全问题,包括数据竞争、死锁和内存可见性,并介绍了相应的解决方法,如使用同步锁、并发容器和原子类等技术,以确保多线程环境下程序的正确性和性能。
72 2
|
9月前
|
安全 Java 容器
多线程(进阶四:线程安全的集合类)
多线程(进阶四:线程安全的集合类)
58 0
|
10月前
|
安全 Java 开发者
Java并发编程中的线程安全性探究
在Java编程中,线程安全性是一个至关重要的问题,涉及到多线程并发访问共享资源时可能出现的数据竞争和不一致性问题。本文将深入探讨Java并发编程中的线程安全性,介绍常见的线程安全性问题以及解决方法,帮助开发者更好地理解和应对在多线程环境下的挑战。