Java算法-力扣leetcode-45. 跳跃游戏 II

一、java8原子类LongAdder

在Java并发编程(2)中有关于AtomicInteger的介绍. AtomicInteger通过CAS非阻塞的原子操作提升了并发性能. 但随着线程数量增大对共享资源的竞争提升,大量线程竞争失败会进入到自旋中,消耗了cpu资源.

JDK8中新增了原子类LongAdder, 弥补AtomicLong的上述缺点. AtomicInteger的缺陷在于共享资源只有一个,LongAdder的思路是将共享资源复制多份,让多个资源去被竞争.在使用的时候再把多个共享资源的数据整合计算得到真实的数据.

从原码具体看下实现

LongAdder longAdder = new LongAdder();
for (int i = 0; i < 10000; i++) {longAdder.add(1);
}

LongAdder的继承关系

public class LongAdder extends Striped64 implements Serializable

构造函数

public LongAdder() {
}

add方法

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);}
}

Cell[] as这里的cells是父类定义的,cells可以理解成上边说的共享资源复制了多份.保存在一个数组中

transient volatile Cell[] cells;

Cell的定义

  • 使用Contended修饰,避免伪共享
  • 其余代码和上一章介绍的atomicInteger内存基本一致
@sun.misc.Contended static final class Cell {//内部维护了long类型的valuevolatile long value;//构造函数Cell(long x) { value = x; }//CAS操作final boolean cas(long cmp, long val) {return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);}// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;//变量内存地址较当前对象的地址偏移量private static final long valueOffset;//初始化valueOffsetstatic {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> ak = Cell.class;valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value"));} catch (Exception e) {throw new Error(e);}}
}

继续看add方法:

  • 如果cells为空, 就执行casBase
  • base也是父类定义的,是基值变量. 一开始并非不高的情况下不会创建多个副本cells. 只会对基础值base做操作.
  • casBase即熟悉的cas原子非阻塞操作
 if ((as = cells) != null || !casBase(b = base, b + x)) {
final boolean casBase(long cmp, long val) {return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);
}
  • uncontended:表示运算操作是否成功
  • m是cells的数组大小
  • a = as[getProbe() & m]是获取当前线程可操作的cell
  • 如果获取到了cell 就进行cas操作, 操作结果可能为false cas可能自旋几次会失败
  • 失败了或者没有获取到cell执行longAccumulate
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);

longAccumulate的内容特别多,这里会对cells数组初始化和扩容

final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended) {//for循环前主要获取h的值。h表示当前线程的hash值 对应在cells下标                 int h;if ((h = getProbe()) == 0) {ThreadLocalRandom.current(); // force initializationh = getProbe();//true表示没有竞争wasUncontended = true;}//是否需要扩容boolean collide = false;    // True if last slot nonempty//CAS的for循环 for (;;) {Cell[] as; Cell a; int n; long v;if ((as = cells) != null && (n = as.length) > 0) {//当前线程对应的cess是否为空if ((a = as[(n - 1) & h]) == null) {//cellsBusy是标识当前是否有线程在对cells在做初始化或者扩容.if (cellsBusy == 0) {       // Try to attach new Cell创建一个CellCell r = new Cell(x);   // Optimistically create//cellsBusy标识当前没有线程在对cells在做初始化或者扩容. 并将                           cellsBusy标识置为1if (cellsBusy == 0 && casCellsBusy()) {boolean created = false;try {               // Recheck under lockCell[] rs; int m, j;if ((rs = cells) != null &&(m = rs.length) > 0 &&rs[j = (m - 1) & h] == null) {//将新建的cell加进数组rs[j] = r;created = true;}} finally {cellsBusy = 0;}//创建成功了跳出循环if (created)break;//失败了就是当前位置已经有了cell继续循环continue;           // Slot is now non-empty}}//获取cellsBusy标识失败collide = false;}//上面位置已经有了cellelse if (!wasUncontended)    // CAS already known to fail//是否存在竞争标志设为truewasUncontended = true;      // Continue after rehash//未发生锁竞争 尝试将值累加在a上 else if (a.cas(v = a.value, ((fn == null) ? v + x :fn.applyAsLong(v, x))))//尝试成功                         break;else if (n >= NCPU || cells != as)collide = false;            // At max size or staleelse if (!collide)collide = true;else if (cellsBusy == 0 && casCellsBusy()) {try {if (cells == as) {      // Expand table unless stale//扩容Cell[] rs = new Cell[n << 1];for (int i = 0; i < n; ++i)rs[i] = as[i];cells = rs;}} finally {cellsBusy = 0;}collide = false;continue;                   // Retry with expanded table}h = advanceProbe(h);}//初始化cellselse if (cellsBusy == 0 && cells == as && casCellsBusy()) {boolean init = false;try {                           // Initialize tableif (cells == as) {Cell[] rs = new Cell[2];rs[h & 1] = new Cell(x);cells = rs;init = true;}} finally {cellsBusy = 0;}//初始化成功if (init)break;}//当`Cell`数组竞争激烈时尝试在`base`上进行累加else if (casBase(v = base, ((fn == null) ? v + x :fn.applyAsLong(v, x))))//尝试成功                        break;                          // Fall back on using base}
}

二、并发包中的并发List- CopyOnWriteArrayList

先看下常用的ArrayList在多线程情况下会发生什么

List<Integer> list = new ArrayList<>();
Thread thread = new Thread(new Runnable() {@Overridepublic void run() {for (int i =0 ;  i<100; i++){list.add(i);}}
});Thread thread1 = new Thread(() -> {for (int i =100 ;  i<200; i++){list.add(i);}
});
thread.start();
thread1.start();
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10at java.util.ArrayList.indexOf(ArrayList.java:321)at java.util.ArrayList.contains(ArrayList.java:304)at com.dxm.test.juc.ListTest.main(ListTest.java:40)

报了数组越界异常. ArrayList是可以自动扩容的,为什么会报越界?
下面是原码

transient Object[] elementData; /
public boolean add(E e) {ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;
}
private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);
}

transient:简单说下elementData为什么用elementData修饰. transient作用是序列化和反序列化时忽略elementData. 主要因为容量问题,elementData可能存在空的元素.Arraylist想要屏蔽接口实现的序列化方法 自己实现自定义的序列化方法.

再看下add方法 add后数组元素个数大于当前elementData.length时会进行grow扩容.

当两个线程同时走到这行ensureCapacityInternal(size + 1); 代码时 并且刚好还剩一个位置时添加一个元素, 只有一个线程会成功执行 elementData[size++] = e; 另一个线程就会报数组越界了.

最后看下CopyOnWriteArrayList是如何保证线程安全的

CopyOnWriteArrayList内部有一把ReentrantLock锁

public class CopyOnWriteArrayList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {private static final long serialVersionUID = 8673264195747942595L;/** The lock protecting all mutators */final transient ReentrantLock lock = new ReentrantLock();

add方法。

  • 首先获取锁
  • 将数组copy一份
  • 修改完再将引用指向新数组
    其他修改 删除方法实现基本都一致, 这里之所以复制了一份数组是因为读操作没有锁,防止读的同时将原数组的某个元素修改了. CopyOnWriteArrayList的读操作是弱一致性的.
public boolean add(E e) {final ReentrantLock lock = this.lock;lock.lock();try {Object[] elements = getArray();int len = elements.length;Object[] newElements = Arrays.copyOf(elements, len + 1);newElements[len] = e;setArray(newElements);return true;} finally {lock.unlock();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/734311.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人不想做事情的时候,可能是他/她正在经历情绪

目录 情绪是什么? 计算机也有“情绪”? 情绪如何影响人的行为? 受情绪困扰时人为何不想做事? 如何排解情绪困扰?

基于javaweb+springboot开发的城市地名地址信息管理系统设计和实现

基于javaweb(springboot)城市地名地址信息管理系统设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言…

基于GAN对抗网进行图像修复

一、简介 使用PyTorch实现的生成对抗网络&#xff08;GAN&#xff09;模型&#xff0c;包括编码器&#xff08;Encoder&#xff09;、解码器&#xff08;Decoder&#xff09;、生成器&#xff08;ResnetGenerator&#xff09;和判别器&#xff08;Discriminator&#xff09;。…

Dubbo的线程池策略

1 Dubbo的线程池概述 这里将要讲述的线程池指Dubbo服务端使用某些线程模型&#xff08;如 all 模型&#xff09;时用到的业务线程池。ThreadPool 是一个扩展接口SPI。 SPI(value "fixed", scope ExtensionScope.FRAMEWORK) public interface ThreadPool {/*** Th…

基于深度学习的苹果叶片病害检测系统(含UI界面、yolov8、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8 yolov8主要包含以下几种创新&#xff1a;         1. 可以任意更换主干结构&#xff0c;支持几百种网络主干。 数据集&#xff1a;     网上下载的数据集&#x…

基于极大似然算法的系统参数辨识matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于极大似然算法的系统参数辨识。对系统的参数a1&#xff0c;b1&#xff0c;a2&#xff0c;b2分别进行估计&#xff0c;计算估计误差以及估计收敛曲线&#xff0…

使用yarn创建vite+vue3electron多端运行

文章目录 第一步 使用yarn创建vite+vue3项目遇到创建报错看第二步 引入electron第三步 创建main.js在electron下面的main.js写入下面代码第四步 安装同时运行多条命令npm包&&修改package.json文件npm包增加一条electron运行脚本命令效果图第一步 使用yarn创建vite+vue3…

【pytorch可视化工具】

TensorboardX 在PyTorch中&#xff0c;模型训练的可视化通常通过TensorBoard或Visdom等工具实现。以下是如何使用TensorBoard进行模型训练可视化的步骤&#xff1a; 使用TensorboardX与PyTorch配合 确保已经安装了tensorboard和tensorboardX库。 pip install tensorboard p…

springboot Mongo大数据查询优化方案

前言 因为项目需要把传感器的数据保存起来&#xff0c;当时设计的时是mongo来存储&#xff0c;后期需要从mongo DB里查询传感器的数据记录。由于传感器每秒都会像mongo数据库存500条左右的数据&#xff0c;1天就有4320万条数据&#xff0c;要想按照时间条件去查询&#xff0c;…

【数据可视化】动手用matplotlib绘制关联规则网络图

下载文中数据、代码、绘图结果 文章目录 关于数据绘图函数完整可运行的代码运行结果 关于数据 如果想知道本文的关联规则数据是怎么来的&#xff0c;请阅读这篇文章 绘图函数 Python中似乎没有很方便的绘制网络图的函数。 下面是本人自行实现的绘图函数&#xff0c;如果想…

(每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)

项目建议与立项申请、初步可行性研究、详细可行性研究、评估与决策是项目投资前使其的四个阶段。在实际工作中&#xff0c;初步可行性研究和详细可行性研究可以依据项目的规模和繁简程度合二为一&#xff0c;但详细可行性研究是不可缺少的。升级改造项目制作初步和详细研究&…

windows11配置电脑IP

windows11配置电脑IP 选择"开始>设置>“网络&Internet >以太网”。在 "属性"下&#xff0c;编辑IP地址&#xff0c;子网掩码&#xff0c;网关以及DNS。

数据库系统原理实验报告1 | E-R图设计

整理自博主2021级专业课《数据库系统原理》自己完成的实验报告。 目录 一、实验目的 二、实验内容 1、某个学校有若干个系 2、某工厂生产若干产品 3、某学校的田径运动会中设置了各类比赛 4、自己调查一个需要提供开发数据库应用系统的单位 三、实验结果总结 四、实验结…

C# sort面试题目

在C#的面试中&#xff0c;排序是一个常见的主题&#xff0c;因为它不仅涉及对集合的基本操作&#xff0c;也反映了应聘者对算法和数据结构的理解。以下是一些关于C#排序的面试题目&#xff1a; 解释C#中List<T>.Sort()方法和Array.Sort()方法的不同之处。 List<T>…

Java中常用的集合及方法(2)

在Java&#xff08;JDK8&#xff09;中&#xff0c;集合&#xff08;Collection&#xff09;是数据结构的实现&#xff0c;用于存储和操作对象集合。 集合&#xff08;Collection&#xff09;中包含的一般类或接口&#xff1a; 在这其中呢&#xff0c;我们经常使用的其实就是L…

第二十二 查询、检索、搜索

查询在计算机中十分广泛的应用。 在字符串或者文本文件中查询关键字&#xff0c;模式匹配&#xff0c;正则表达式。在数组、树、哈希表等数据结构中查询指定数据在数据库中查询在海量非结构文件中查询搜索引擎 模式匹配 模式匹配是数据结构中字符串的一种基本运算&#xff0…

API 设计的原则

我在 Thoughtworks 上曾经读到过一篇文章《API 设计的几条原则》我觉得写得非常不错&#xff0c;链接我放底下&#xff0c;推荐阅读。 在我多年的工作历程中&#xff0c;我深刻意识到 API 的设计至关重要&#xff0c;但是我发现&#xff0c;在一般的组织中&#xff0c;API 的设…

K8S - 在任意node里执行kubectl 命令

当我们初步安装玩k8s &#xff08;master 带 2 nodes&#xff09; 时 正常来讲kubectl 只能在master node 里运行 当我们尝试在某个 node 节点来执行时&#xff0c; 通常会遇到下面错误 看起来像是访问某个服务器的8080 端口失败了。 原因 原因很简单 , 因为k8s的各个组建&…

外包干了6天后悔了,技术明显进步。。。。。

先说一下自己的情况&#xff0c;本科生&#xff0c;19年通过校招进入广州某软件公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

chrome高内存占用问题

chrome号称内存杀手不是盖的&#xff0c;不设设置的话&#xff0c;经常被它内存耗尽死机是常事。以下自用方法 1 自带的memory saver chrome://settings/performance PerformanceMemory Saver When on, Chromium frees up memory from inactive tabs. This gives active tab…