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

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

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映射到一个特定的数据块。当线程需要访问其本地数据时,它首先需要查找映射表,找到对应的数据块,然后在这个数据块中进行操作。

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

相关文章
|
3月前
|
存储 安全 Java
面试题:再谈Synchronized实现原理!
面试题:再谈Synchronized实现原理!
|
8月前
|
存储 缓存 安全
面试官:说说volatile底层实现原理?
面试官:说说volatile底层实现原理?
506 5
面试官:说说volatile底层实现原理?
|
5月前
|
Java 调度
【多线程面试题十四】、说一说synchronized的底层实现原理
这篇文章解释了Java中的`synchronized`关键字的底层实现原理,包括它在代码块和方法同步中的实现方式,以及通过`monitorenter`和`monitorexit`指令以及`ACC_SYNCHRONIZED`访问标志来控制线程同步和锁的获取与释放。
|
5月前
|
安全 Java
【多线程面试题 六】、 如何实现线程同步?
实现线程同步的方法包括同步方法、同步代码块、使用ReentrantLock、volatile关键字以及原子变量类,以确保线程安全和数据一致性。
|
8月前
|
缓存 安全 Java
多线程--深入探究多线程的重点,难点以及常考点线程安全问题
多线程--深入探究多线程的重点,难点以及常考点线程安全问题
165 1
|
8月前
|
安全 Java 开发者
Java并发编程中的线程安全性探究
在Java编程中,线程安全性是一个至关重要的问题,涉及到多线程并发访问共享资源时可能出现的数据竞争和不一致性问题。本文将深入探讨Java并发编程中的线程安全性,介绍常见的线程安全性问题以及解决方法,帮助开发者更好地理解和应对在多线程环境下的挑战。
|
8月前
|
安全 Java
Java并发编程—并发流程控制与AQS原理及相关源码解析
Java并发编程—并发流程控制与AQS原理及相关源码解析
84 0
|
缓存 Java 编译器
Java并发编程(实战):如何解决可见性和有序性问题
Java 内存模型这个概念,在职场的很多面试中都会考核到,是一个热门的考点,也是一个人并发水平的具体体现。原因是当并发程序出问题时,需要一行一行地检查代码,这个时候,只有掌握 Java 内存模型,才能慧眼如炬地发现问题。 什么是 Java 内存模型? 你已经知道,导致可见性的原因是缓存,导致有序性的原因是编译优化,那解决可见性、有序性最直接的办法就是禁用缓存和编译优化,但是这样问题虽然解决了,我们程序的性能可就堪忧了。
|
存储 安全 Java
java并发原理实战(5)--线程安全性问题和synchronized原理理解
java并发原理实战(5)--线程安全性问题和synchronized原理理解
java并发原理实战(5)--线程安全性问题和synchronized原理理解
|
安全 Java 程序员
面试题:线程安全问题的解决方案有哪些?
线程安全是指某个方法或某段代码,在多线程中能够正确的执行,不会出现数据不一致或数据污染的情况,我们把这样的程序称之为线程安全的,反之则为非线程安全的。在 Java 中,解决线程安全问题有以下 3 种手段: 使用线程安全类,比如 AtomicInteger。 加锁排队执行 使用 synchronized 加锁。 使用 ReentrantLock 加锁。 使用线程本地变量 ThreadLocal。 接下来我们逐个来看它们的实现。
351 0
面试题:线程安全问题的解决方案有哪些?