CAS

Java并发编程之CAS和AQS

依然范特西╮ 提交于 2021-01-29 01:26:49
什么是CAS > CAS(compare and swap) ,字面意思比较并交换,是解决多线程并行情况下使用锁造成性能损耗的一种机制. public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } > CAS 有三个操作数, valueOffset 内存值, expect 期望值, update 要更新的值。如果内存值( valueOffset )和期望值( expect )是一样的。那么处理器会将该位置的值更新为( update ),否则不做任何操作。 > CAS 有效地说明了“我认为位置 valueOffset 应该包含值 expect ,如果包含该值,则将 update 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。”在 Java 中,sun.misc.Unsafe类提供了硬件级别的原子操作来实现这个 CAS,java.util.concurrent包下的大量类都使用了这个Unsafe类的 CAS 操作 CAS的应用 > java.util.concurrent.atomic 包下的类大多数是使用 CAS 实现的,如 AtomicInteger ,

AQS总结

你说的曾经没有我的故事 提交于 2021-01-28 09:18:24
AQS全称为AbstractQueuedSynchronizer,可以叫做队列同步器。 为线程的同步和等待等操作提供一个基础模板类。尽可能多的实现可重入锁,读写锁同步器所有需要的功能。队列同步器内部实现了线程的同步队列,独占或是共享的获取方式等,使其只需要少量的代码便可以实现目标功能。 一般来说,AQS的子类应以其他类的内部类的形式存在,然后使用代理模式调用子类和AQS本身的方法实现线程的同步。 也就是说,使用 ReentrantLock 举例,外界调用 ReentrantLock , ReentrantLock 内部定义 Sync , Sync 是AQS的子类,在 ReentrantLock 的内部实现中调用 Sync 的方法,最后完成最终的功能,当然 ReentrantLock 内部稍复杂,又加入和公平锁和非公平锁。 AQS内部有一个核心状态为 state 。所有通过AQS实现功能的类都是通过修改state的状态来操作线程的同步状态。比如在 ReentrantLock 中,一个锁中只有一个 state 状态,当 state 为0时,代表所有线程没有获取锁,当 state 为1时,代表有线程获取到了锁。通过是否能把 state 从0设置成1,当然,设置的方式是使用CAS设置,代表一个线程是否获取锁成功。 AQS提供了操作state的方法 int getState() void

AQS源码分析

感情迁移 提交于 2021-01-28 04:41:56
小弟为了进阶自己的技术。一点点挖掘自身潜力。 AQS abstractQueueSynchronizer(抽象队列同步器),是什么? 答:它是用来构建锁 或者 其他同步器组件的重量级基础框架,是整个JUC体系的基础。通过内置FIFO队列来完成获取线程取锁的排队工作,并通过一个int类型变量标识持有锁的状态; 前置知识点: 1、可重入锁(递归锁): sync(隐式锁,jvm管理)和ReentrantLock(Lock显式锁,就是手动加解)是重入锁的典型代表,为可以重复使用的锁。一个变成多个流程,可以获取同一把锁。 可重入锁概念: 是指一个线程,在外层方法获取锁的时候,再次进入该线程的内层方法会自动获取锁(必须是同一个对象),不会被阻塞。可避免死锁 举例: 递归调用同一个 sync修饰的方法或者代码块。必须是一个对象才行。一个线程调用一个对象的method1,method1 调用method2,method2调用method3, 3个方法都是被sync修饰,这样也是一个可重入锁的例子 。 再比如下面这种 static Object lock = new Object(); public void mm(){ synchronized (lock){ System.out.println("===========mm method"); synchronized (lock){

Java中的锁分类

≯℡__Kan透↙ 提交于 2021-01-28 04:00:55
在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下: 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 分段锁 偏向锁/轻量级锁/重量级锁 自旋锁 上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,下面总结的内容是对每个锁的名词进行一定的解释。 公平锁/非公平锁 公平锁是指多个线程按照申请锁的顺序来获取锁。 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。 对于Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。 对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。 可重入锁 可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。 对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。 对于Synchronized而言,也是一个可重入锁

ConcurrentMap原理详解

流过昼夜 提交于 2021-01-27 09:53:41
ConcurrentMap原理详解 jdk1.8 数据结构 数组+链表+红黑树 Node<K,V>{} static class Node<K,V> implements Entry<K,V> { final int hash; final K key; volatile V val; volatile Node<K,V> next; 如何线程安全 数组桶值设置和更新使用CAS算法 tabAt(Node<K,V>[] tab, int i) casTabAt(Node<K,V>[] tab, int i, Node<K,V> c, Node<K,V> v) setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) 针对链表和树更新使用Synchronize 设置putVal(K key, V value, boolean onlyIfAbsent) 1、数组为空,先初始化 2、tabAt获取桶位置元素,如果为空,则casTabAt(),期望值为null,新值为新节点 3、桶位置如果存在数据,说明hash冲突了,如果当前节点hash为MOVED说明在扩容,则当前线程辅助扩容 4、如果hash值>=0,说明是链表结构 4.1循环覆盖链表所有key一样节点 4.2循环至链表尾结点时(next为null)则追加到尾结点 5

并发编程之Java锁

江枫思渺然 提交于 2021-01-26 10:10:24
一、重入锁 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized(重量级) 和 ReentrantLock(轻量级)等等 ) 。这些已经写好提供的锁为我们开发提供了便利。 重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。 在JAVA环境下 ReentrantLock 和synchronized 都是 可重入锁 synchronized: public class Test implements Runnable { public synchronized void get() { System.out.println("name:" + Thread.currentThread().getName() + " get();"); } public synchronized void set() { System.out.println("name:" + Thread.currentThread().getName() + " set();"); get(); } @Override public void run() { set(); } public static void main(String[] args) { Test ss = new Test(); new Thread

分布式锁与Redis 分布式锁实现

与世无争的帅哥 提交于 2021-01-26 08:31:56
分布式锁 概念 任何一个系统都无法同时满足一致性(Consistency),可用性(Availability),分区容错性(Partition tolerance), 只能同时满足2个; 分布式锁就是为了解决数据一致性问题. 悲观锁和乐观锁 悲观锁: 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次拿数据都会上锁,这样别人想拿这个数据就会阻塞,知道锁被释放 悲观锁多用于多写场景,关系型数据库中很多使用了这种悲观锁机制 实现: redis 实现锁机制 乐观锁 总是假设最好的情况,即每次去拿数据的时候都认为别的线程不会去修改,所以不会上锁,但是在更新数据的时候会判断在此期间有没有其它线程更新了这个数据,可以用版本号机制和CAS算法来实现; 乐观锁多用户多读场景,提高吞吐量,比如数据库提供的write_condition机制 实现: 数据库添加版本号字段: 一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加1。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。 CAS 算法 应用场景 涉及到多个实例进程操作同一份数据时就需要用到锁机制,比如: 下单,修改库存,更新缓存等

Java高并发BlockingQueue重要的实现类二

为君一笑 提交于 2021-01-23 23:51:42
DelayQueue > DelayQueue是一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的Delayed元素。 > 存放到DelayDeque的元素必须继承Delayed接口。Delayed接口使对象成为延迟对象,它使存放在DelayQueue类中的对象具有了激活日期,该接口强制执行下列两个方法: CompareTo(Delayed o):Delayed接口继承了Comparable接口,因此有了这个方法 getDelay(TimeUnit unit):这个方法返回到激活日期的剩余时间,时间单位由单位参数指定 DelayQueue使用场景 关闭空闲链接。服务器中,有很多客户端链接,空闲一段时间后需要关闭。 缓存超过了缓存时间,就需要从缓存中移除。 DelayQueue超时订单处理案例 package com.rumenz.learn.delayqueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; //DelayQueue里面的元素必须实现Delayed public class Item<t> implements Delayed { private Long expireTime; private T data;

线程池之ThreadPoolExecutor线程池源码分析笔记

萝らか妹 提交于 2021-01-22 16:33:13
1.线程池的作用 一方面当执行大量异步任务时候线程池能够提供较好的性能,在不使用线程池的时候,每当需要执行异步任务时候是直接 new 一线程进行运行,而线程的创建和销毁是需要开销的。使用线程池时候,线程池里面的线程是可复用的,不会每次执行异步任务时候都重新创建和销毁线程。 另一方面线程池提供了一种资源限制和管理的手段,比如可以限制线程的个数,动态新增线程等,每个 ThreadPoolExecutor 也保留了一些基本的统计数据,比如当前线程池完成的任务数目等。 2.ThreadPoolExecutor 原理探究 类图如下: 如上类图,Executors 其实是个工具类,里面提供了好多静态方法,根据用户选择返回不同的线程池实例。 ThreadPoolExecutor 继承了 AbstractExecutorService ,成员变量 ctl 是个 Integer 的原子变量用来记录线程池状态 和 线程池中线程个数,类似于 ReentrantReadWriteLock 使用一个变量存放两种信息。 这里假设 Integer 类型是 32 位二进制标示,则其中高 3 位用来表示线程池状态,后面 29 位用来记录线程池线程个数。 // 用来标记线程池状态(高3位),线程个数(低29位) // 默认是RUNNING状态,线程个数为0 private final AtomicInteger

线程池 ThreadPoolExecutor 原理及源码笔记

跟風遠走 提交于 2021-01-22 16:32:31
前言 " 前面在学习 JUC 源码时,很多代码举例中都使用了线程池 ThreadPoolExecutor ,并且在工作中也经常用到线程池,所以现在就一步一步看看,线程池的源码,了解其背后的核心原理。 " 1 介绍 什么是线程池 " 线程池(英语:thread pool :一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 —— 维基百科 为什么要使用线程池 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。 提高响应速度:任务到达时,无需等待线程创建即可立即执行。 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。 如何使用线程池 线程池使用有很多种方式,不过按照《Java 开发手册》描述,尽量还是要使用 ThreadPoolExecutor 进行创建。 代码举例: ExecutorService pool = new ThreadPoolExecutor( 5 ,