Java并发——原子变量和原子操作】的更多相关文章

很多情况下我们只是需要一个简单的.高效的.线程安全的递增递减方案.注意,这里有三个条件:简单,意味着程序员尽可能少的操作底层或者实现起来要比较容易:高效意味着耗用资源要少,程序处理速度要快:线程安全也非常重要,这个在多线程下能保证数据的正确性.这三个条件看起来比较简单,但是实现起来却难以令人满意. 通常情况下,在Java里面,++i或者--i不是线程安全的,这里面有三个独立的操作:获得变量当前值,为该值+1/-1,然后写回新的值.在没有额外资源可以利用的情况下,只能使用加锁才能保证读-改-写这三…
3. 原子操作与CAS 3.1 原子操作 所谓原子操作是指不会被线程调度机制打断的操作:这种操作一旦开始,就一直运行到结束,中间不会有任何context switch,也就是切换到另一个线程. 为了实现原子操作,Java中可以通过synchronized关键字将函数或者代码块包围,以实现操作的原子性.但是synchronized关键字有一些很显著的问题: 1.synchronized是基于阻塞锁的机制,如果被阻塞的线程优先级很高,可能很长时间其他线程都没有机会运行: 2.拿到锁的线程一直不释放锁…
java的原子变量类似c++的InterlockedDecrement()操作.其实就是在进行算术时,把整个算式看为一个整体,并且保证同一时间只计算该式子一次. 它的用途比如,多个线程可能会调用某个函数.但是现在只希望它被调用一次后就不被调用了.此时就可以用原子变量来控制,它比对整个方法加锁更加高效. 1.private AtomicInteger account = new AtomicInteger(1); //声明 2.private void fun() { if(m_nStopCoun…
   原子变量      原子变量保证了该变量的所有操作都是原子的,不会因为多线程的同时访问而导致脏数据的读取问题      Java给我们提供了以下几种原子类型: AtomicInteger和AtomicIntegerArray:基于Integer类型 AtomicBoolean:基于Boolean类型 AtomicLong和AtomicLongArray:基于Long类型 AtomicReference和AtomicReferenceArray:基于引用类型 1.非原子操作 package…
主要概念:线程安全性.原子性.原子变量.原子操作.竟态条件.复合操作.加锁机制.重入.活跃性与性能. 1.当多个线程访问某个状态变量并且其中有一个线程执行写入操作时,必须采用同步机制来协同这些线程对变量的访问.Java中的主要同步机制是关键字synchronized,他提供了一种独占的加锁方式. 2.当设计线程安全的类时,良好的面相对象技术.不可修改性.以及明晰的不变性规范都能启动一定的帮助作用. 3.线程安全类的定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替…
代码中比较容易出现bug的场景: 不一致的同步,直接调用Thread.run,未被释放的锁,空的同步块,双重检查加锁,在构造函数中启动一个线程,notify或notifyAll通知错误,Object.wait和Condition.await未在同步方法或块中调用,把Lock当锁用,调用Condition.wait方法,在休眠或等待时持有锁,自旋循环. 1.多线程可以提高资源的利用率,可以充分利用现代多核处理器的特性,让每个线程负责处理同类型的任务,更加容易维护,同时通过异步处理提高响应性. 2.…
一.锁机制存在的问题 (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换和调度延时,引起性能问题. (2)一个线程持有锁会导致其它所有需要此锁的线程挂起. (3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险. ​ volatile是不错的机制,但是volatile不能保证原子性.因此对于同步最终还是要回到锁机制上来. ​ 独占锁是一种悲观锁,synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁.而另一个更加有效…
以下内容转自http://ifeve.com/non-blocking-algorithms/: 在并发上下文中,非阻塞算法是一种允许线程在阻塞其他线程的情况下访问共享状态的算法.在绝大多数项目中,在算法中如果一个线程的挂起没有导致其它的线程挂起,我们就说这个算法是非阻塞的. 为了更好的理解阻塞算法和非阻塞算法之间的区别,我会先讲解阻塞算法然后再讲解非阻塞算法. 阻塞并发算法 一个阻塞并发算法一般分下面两步: 执行线程请求的操作 阻塞线程直到可以安全地执行操作 很多算法和并发数据结构都是阻塞的.…
8. JMM和底层实现原理 8.1 线程间的通信与同步 线程之间的通信 线程的通信是指线程之间以何种机制来交换信息.在编程中,线程之间的通信机制有两种,共享内存和消息传递. 在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信,典型的共享内存通信方式就是通过共享对象进行通信. 在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信,在java中典型的消息传递方式就是wait()和notify(). 线程之间的同步…
JDK 中基于链表的阻塞队列 LinkedBlockingQueue 原理剖析,LinkedBlockingQueue 内部是如何使用两个独占锁 ReentrantLock 以及对应的条件变量保证多线程先入队出队操作的线程安全?为什么不使用一把锁,使用两把为何能提高并发度? LinkedBlockingQueue的实现是使用独占锁实现的阻塞队列.首先看一下LinkedBlockingQueue 的类图结构,如下图所示: 如类图所示:LinkedBlockingQueue是使用单向链表实现,有两个…
5 并发容器 5.1 Hashtable.HashMap.TreeMap.HashSet.LinkedHashMap 在介绍并发容器之前,先分析下普通的容器,以及相应的实现,方便后续的对比. Hashtable.HashMap.TreeMap 都是最常见的一些 Map 实现,是以键值对的形式存储和操作数据的容器类型. Hashtable 是早期 Java 类库提供的一个哈希表实现,本身是同步的,不支持 null 键和值,由于同步导致的性能开销,所以已经很少被推荐使用. HashMap 是应用更加…
4 显示锁和AQS 4.1 Lock接口 核心方法 Java在java.util.concurrent.locks包中提供了一系列的显示锁类,其中最基础的就是Lock接口,该接口提供了几个常见的锁相关的操作. public interface Lock { void lock(); void lockInterruptibly() throws InterruptedException; boolean tryLock(); boolean tryLock(long time, TimeUnit…
1.并发编程基础 1.1 基本概念 CPU核心与线程数关系 Java中通过多线程的手段来实现并发,对于单处理器机器上来讲,宏观上的多线程并行执行是通过CPU的调度来实现的,微观上CPU在某个时刻只会运行一个线程.事实上,如果这些任务不存在阻塞,也就是程序中的某个任务因为该程序控制范围之外的某些条件(通常是I/O)而导致不能继续执行,由于在任务之间切换会产生开销,因此并行的效率可能没有顺序执行的效率高,并行也就没有意义. 一般来讲,CPU核心数和线程数的关系为核心数:线程数=1:1:但是如果使用了…
6. 线程池 6.1 基本概念 在web开发中,服务器需要接受并处理请求,所以会为一个请求来分配一个线程来进行处理.如果每次请求都新创建一个线程的话实现起来非常简便,但是存在一个问题:如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁线程,如此一来会大大降低系统的效率.可能出现服务器在为每个请求创建新线程和销毁线程上花费的时间和消耗的系统资源要比处理实际的用户请求的时间和资源更多. 那么有没有一种办法使执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?这就是线程…
7. 线程安全 7.1 线程安全的定义 如果多线程下使用这个类,不过多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的. 类的线程安全表现为: 操作的原子性 内存的可见性 不做正确的同步,在多个线程之间共享状态的时候,就会出现线程不安全. 7.2 如何保证线程安全 栈封闭 所有的变量都是在方法内部声明的,这些变量都处于栈封闭状态. 比如下面的例子,a和b都是在方法内部定义的,无法被外部线程所访问,当方法结束后,栈内存被回收,所以是线程安全的. void fun(){ i…
9.1 CompletableFuture CompletableFuture是JDK 8中引入的工具类,实现了Future接口,对以往的FutureTask的功能进行了增强. 手动设置完成状态 CompletableFuture和Future一样,可以作为函数调用的契约,当向CompletableFuture请求数据时,如果数据还没有准备好,请求线程就会等待.但是,我们可以手动设置CompletableFuture的完成状态. 下面的例子中,创建了CompletableFuture对象实例进行…
2.线程的并发工具类 2.1 Fork-Join JDK 7中引入了fork-join框架,专门来解决计算密集型的任务.可以将一个大任务,拆分成若干个小任务,如下图所示: Fork-Join框架利用了分而治之的思想:什么是分而治之?规模为N的问题,N<阈值,直接解决,N>阈值,将N分解为K个小规模子问题,子问题互相对立,与原问题形式相同,将子问题的解合并得到原问题的解. 具体使用中,需要向ForkJoinPool线程池提交一个ForkJoinTask任务.ForkJoinTask任务有两个重要…
非阻塞算法:使用底层的原子机器指令(例如比较并交换指令)代替锁来确保数据在并发访问中的一致性 应用于在操作系统和JVM中实现线程 / 进程调度机制.垃圾回收机制以及锁和其他并发数据结构 可伸缩性和活跃性上拥有巨大的优势,不存在死锁 原子变量:提供了与volatile类型变量相同的内存语义,并支持原子的更新操作,比基于锁的方法提供更高的可伸缩性 一.锁的劣势 锁:独占方式访问共享变量,对变量的操作对其他获得同一个锁的线程可见 劣势: 请求锁失败,一些线程将被挂起并且在稍后恢复运行 恢复执行时必须等…
原子变量和非阻塞的同步机制 一.锁的劣势 1.在多线程下:锁的挂起和恢复等过程存在着很大的开销(及时现代的jvm会判断何时使用挂起,何时自旋等待) 2.volatile:轻量级别的同步机制,但是不能用于构建原子复合操作 因此:需要有一种方式,在管理线程之间的竞争时有一种粒度更细的方式,类似与volatile的机制,同时还要支持原子更新操作 二.CAS 独占锁是一种悲观的技术--它假设最坏的情况,所以每个线程是独占的 而CAS比较并交换:compareAndSwap/Set(A,B):我们认为内存…
这篇说说java.util.concurrent.atomic包里的类,总共12个.网上有非常多文章解析这几个类.这里挑些重点说说. watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSVRlcl9aQw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt=""> 这12个类能够分为三组: 1. 普通类型的原子变量 2. 数组…
非阻塞的同步机制 简单的说,那就是又要实现同步,又不使用锁. 与基于锁的方案相比,非阻塞算法的实现要麻烦的多,但是它的可伸缩性和活跃性上拥有巨大的优势. 实现非阻塞算法的常见方法就是使用volatile语义和原子变量. 硬件对并发的支持 原子变量的产生主要是处理器的支持,最重要的是大多数处理器架构都支持的CAS(比较并交换)指令. 模拟实现AtomicInteger的++操作 首先我们模拟处理器的CAS语法,之所以说模拟,是因为CAS在处理器中是原子操作直接支持的.不需要加锁. public s…
一.锁的劣势 锁定后假设未释放.再次请求锁时会造成堵塞.多线程调度通常遇到堵塞会进行上下文切换,造成很多其它的开销. 在挂起与恢复线程等过程中存在着非常大的开销,而且通常存在着较长时间的中断. 锁可能导致优先级反转,即使较高优先级的线程能够抢先运行,但仍然须要等待锁被释放,从而导致它的优先级会降至低优先级线程的级别. 二.硬件对并发的支持 处理器填写了一些特殊指令,比如:比較并交换.关联载入/条件存储. 1 比較并交换 CAS的含义是:"我觉得V的值应该为A.假设是.那么将V的值更新为B,否则不…
原子变量最主要的一个特点就是所有的操作都是原子的,synchronized关键字也可以做到对变量的原子操作.只是synchronized的成本相对较高,需要获取锁对象,释放锁对象,如果不能获取到锁,还需要阻塞在阻塞队列上进行等待.而如果单单只是为了解决对变量的原子操作,建议使用原子变量.关于原子变量的介绍,主要涉及以下内容: 原子变量的基本概念 通过AtomicInteger了解原子变量的基本使用 通过AtomicInteger了解原子变量的基本原理 AtomicReference的基本使用 使…
先上一段代码: package test; public class Program { public static int i = 0; private static class Next extends Thread { public void run() { i = i + 1; System.out.println(i); } } public static void main(String[] args) { Thread[] threads = new Thread[10]; for…
1.非阻塞算法 非阻塞算法属于并发算法,它们可以安全地派生它们的线程,不通过锁定派生,而是通过低级的原子性的硬件原生形式 -- 例如比较和交换.非阻塞算法的设计与实现极为困难,但是它们能够提供更好的吞吐率,对生存问题(例如死锁和优先级反转)也能提供更好的防御.使用底层的原子化机器指令取代锁,比如比较并交换(CAS,compare-and-swap). 2.悲观技术 独占锁是一种悲观的技术.它假设最坏的情况发生(如果不加锁,其它线程会破坏对象状态),即使没有发生最坏的情况,仍然用锁保护对象状态.…
如果线程本地的计算量较少,那么在锁和原子变量上的竞争将非常激烈.如果线程本地的计算量较多,那么在锁和原子变量上的竞争会降低,因为在线程中访问锁和原子变量的频率将降低. 在高度竞争的情况下,锁的性能将超过原子变量的性能.在中低程度的竞争下,原子变量能提供更高的可伸缩性.而在高强度的竞争下,锁能够更有效地避免竞争.性能比较下两幅图.两图中第三条曲线使用ThreadLocal即每个线程都只能看到自己私有状态变量,不是所有线程共享同一个状态变量,说明如果能哆避免使用共享状态,那么开销将会更小.我们可以通…
我们认为,由于思维定式原子变量总是比同步运行的速度更快,我想是这样也已经,直到实现了ID在第一次测试过程生成器不具有在这样一个迷迷糊糊的东西. 测试代码: import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class ConcurrentAdder { private static final AtomicInteger ATOMI…
综述 JDK从1.5开始提供了java.util.concurrent.atomic包. 通过包中的原子操作类能够线程安全地更新一个变量. 包含4种类型的原子更新方式:基本类型.数组.引用.对象中字段更新. atomic包中的原子操作类基本上内部都是使用Unsafe类实现的,原子更新的实质其实就是获取内存偏移地址,对地址中的变量进行更新. 关于Unsafe类,可以参考我的这篇博文[Java并发]Java中的Unsafe类 1.原子更新基本类型类 atomic包内包含AtomicBoolean.A…
来自<java并发编程的艺术>.只是方便自己以后查找. 处理器如何实现原子操作 32位IA-32处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作.首先处理器会自动保证基本的内存操作的原子性.处理器保证从系统内存中读取或者写入一个字节是原子的,意思是当一个处理器读取一个字节时,其他处理器不能访问这个字节的内存地址.Pentium 6和最新的处理器能自动保证单处理器对同一个缓存行里进行16/32/64位的操作是原子的,但是复杂的内存操作处理器是不能自动保证其原子性的,比如跨总线…
1. 原子更新基本类型类 2. 原子更新数组 3. 原子更新引用 4. 原子更新属性 5. txt java 原子操作类Atomic 概述 java.util.concurrent.atomic里的原子操作类提供了线程安全地更新一个变量的方式 4大类型13个原子操作类 基本类型类 AtomicBoolean AtomicInteger AtomicLong 数组 AtomicIntegerArray AtomicLongArray AtomicReferenceArray 引用 AtomicRe…