java八股文复习----java集合,CAS---2024/03/12

news2025/1/12 23:33:08

1.java常见的集合类

在这里插入图片描述

2.List,Set,Map的区别

在这里插入图片描述

3.上述三个集合有哪些常用的方法

在这里插入图片描述

4.List,Set,Map哪几个是线程安全的?

在这里插入图片描述

5.ArrayList和LinkedList的区别

在这里插入图片描述

6.ArrayList和Vector的区别

在这里插入图片描述

7.ArrayList的扩容机制

在这里插入图片描述

8.HashMap集合

8.1数据结构

在这里插入图片描述

8.2哈希冲突的解决办法有哪些

在这里插入图片描述

8.3什么时候使用链表,什么时候使用红黑树?

在这里插入图片描述

8.4为什么使用红黑树而不是二叉树或者平衡二叉树?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9.HashMap是否线程安全?是如何解决的?

在这里插入图片描述
在这里插入图片描述

10.HashMap的实现原理

1.HashMap在Jdk1.8以后是基于数组+链表+红黑树来实现的,特点是,key不能重复,可以为null,线程不安全

2.HashMap的扩容机制

HashMap的默认容量为16,默认的负载因子为0.75,当HashMap中元素个数超过容量乘以负载因子的个数时,就创建一个大小为前一次两倍的新数组,再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时,链表转为红黑树

3.HashMap存取原理

(1)计算key的hash值,然后进行二次hash,根据二次hash结果找到对应的索引位置

(2)如果这个位置有值,先进性equals比较,若结果为true则取代该元素,若结果为false,就使用高低位平移法将节点插入链表(JDK8以前使用头插法,但是头插法在并发扩容时可能会造成环形链表或数据丢失,而高低位平移发会发生数据覆盖的情况)
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/w20001118/article/details/125724647

11.ConcurrentHashMap原如何保证的线程安全?

JDK1.7:使用分段锁,将一个Map分为了16个段,每个段都是一个小的hashmap,每次操作只对其中一个段加锁

JDK1.8:采用CAS+Synchronized保证线程安全,每次插入数据时判断在当前数组下标是否是第一次插入,是就通过CAS方式插入,然后判断f.hash是否=-1,是的话就说明其他线程正在进行扩容,当前线程也会参与扩容;删除方法用了synchronized修饰,保证并发下移除元素安全


CAS补充资料
我们常说的 CAS 自旋锁是什么
CAS(Compare and swap),即比较并交换,也是实现我们平时所说的自旋锁或乐观锁的核心操作。

它的实现很简单,就是用一个旧的预期的值和内存值进行比较,如果两个值相等,就用新的值替换内存值,并返回 true。否则,返回 false。

保证原子操作
任何技术的出现都是为了解决某些特定的问题, CAS 要解决的问题就是保证原子操作。原子操作是什么,原子就是最小不可拆分的,原子操作就是最小不可拆分的操作,也就是说操作一旦开始,就不能被打断,直到操作完成。在多线程环境下,原子操作是保证线程安全的重要手段。举个例子来说,假设有两个线程在工作,都想对某个值做修改,就拿自增操作来说吧,要对一个整数 i 进行自增操作,需要基本的三个步骤:

1、读取 i 的当前值;

2、对 i 值进行加 1 操作;

3、将 i 值写回内存;

假设两个进程都读取了 i 的当前值,假设是 0,这时候 A 线程对 i 加 1 了,B 线程也 加 1,最后 i 的是 1 ,而不是 2。这就是因为自增操作不是原子操作,分成的这三个步骤可以被干扰。如下面这个例子,10个线程,每个线程都执行 10000 次 i++ 操作,我们期望的值是 100,000,但是很遗憾,结果总是小于 100,000 的。

   static int i = 0;
    
   public static void add(){
        i++;
    }
        private static class Plus implements Runnable{

   @Override
        public void run(){
            for(int k = 0;k<10000;k++){
                add();
            }
        }
    }   
 public static void main(String[] args) throws InterruptedException{
        Thread[] threads = new Thread[10];
        for(int i = 0;i<10;i++){
            threads[i] = new Thread(new Plus());
            threads[i].start();
        }

 for(int i = 0;i<10;i++){
            threads[i].join();
   }
    System.out.println(i);
 }

既然这样,那怎么办。没错,也许你已经想到了,可以加锁或者利用 synchronized 实现,例如,将 add() 方法修改为如下这样:

public synchronized static void add(){
        i++;
    }

或者,加锁操作,例如下面使用 ReentrantLock (可重入锁)实现。

private static Lock lock = new ReentrantLock();
    public static void add(){
        lock.lock();
        i++;
        lock.unlock();
    }

CAS 实现自旋锁
既然用锁或 synchronized 关键字可以实现原子操作,那么为什么还要用 CAS 呢,因为加锁或使用 synchronized 关键字带来的性能损耗较大,而用 CAS 可以实现乐观锁,它实际上是直接利用了 CPU 层面的指令,所以性能很高。

上面也说了,CAS 是实现自旋锁的基础,CAS 利用 CPU 指令保证了操作的原子性,以达到锁的效果,至于自旋呢,看字面意思也很明白,自己旋转,翻译成人话就是循环,一般是用一个无限循环实现。这样一来,一个无限循环中,执行一个 CAS 操作,当操作成功,返回 true 时,循环结束;当返回 false 时,接着执行循环,继续尝试 CAS 操作,直到返回 true。

其实 JDK 中有好多地方用到了 CAS ,尤其是 java.util.concurrent包下,比如 CountDownLatch、Semaphore、ReentrantLock 中,再比如 java.util.concurrent.atomic 包下,相信大家都用到过 Atomic* ,比如 AtomicBoolean、AtomicInteger 等。

12.CAS


什么是CAS

CAS(Compare-and-Swap)是一种乐观锁的实现方式,全称为“比较并交换”,是一种无锁的原子操作。

在并发编程中,我们都知道i++操作是非线程安全的,这是因为 i++操作不是原子操作,我们之前在讲多线程带来了什么问题中有讲到,大家应该还记得吧?

如何保证原子性呢?

常见的做法就是加锁。

在 Java 中,我们可以使用 synchronized关键字 和 CAS(Compare-and-Swap)来实现加锁效果。

**synchronized 是悲观锁,**尽管随着 JDK 版本的升级,synchronized 关键字已经“轻量级”了很多(前面有细讲,戳链接回顾),但依然是悲观锁,线程开始执行第一步就要获取锁,一旦获得锁,其他的线程进入后就会阻塞并等待锁。

CAS 是乐观锁,线程执行的时候不会加锁,它会假设此时没有冲突,然后完成某项操作;如果因为冲突失败了就重试,直到成功为止。

12.1乐观锁与悲观锁

锁可以从不同的角度来分类。比如我们在前面讲 synchronized 四种锁状态的时候,提到过偏向锁、轻量级锁、重量级锁,对吧?乐观锁和悲观锁也是一种分类方式。

悲观锁

对于悲观锁来说,它总是认为每次访问共享资源时会发生冲突,所以必须对每次数据操作加上锁,以保证临界区的程序同一时间只能有一个线程在执行。

乐观锁

乐观锁,顾名思义,它是乐观派。乐观锁总是假设对共享资源的访问没有冲突,线程可以不停地执行,无需加锁也无需等待。一旦多个线程发生冲突,乐观锁通常使用一种称为 CAS 的技术来保证线程执行的安全性。

由于乐观锁假想操作中没有锁的存在,因此不太可能出现死锁的情况,换句话说,乐观锁天生免疫死锁。


乐观锁多用于“读多写少“的环境,避免频繁加锁影响性能;
悲观锁多用于”写多读少“的环境,避免频繁失败和重试影响性能。


12.2什么是 CAS

在 CAS 中,有这样三个值:

V:要更新的变量(var)
E:预期值(expected)
N:新值(new)

比较并交换的过程如下:

判断 V 是否等于 E,如果等于,将 V 的值设置为 N;如果不等,说明已经有其它线程更新了 V,于是当前线程放弃更新,什么都不做。

这里的预期值 E 本质上指的是“旧值”。

我们以一个简单的例子来解释这个过程:

如果有一个多个线程共享的变量i原本等于 5,我现在在线程 A 中,想把它设置为新的值 6;
我们使用 CAS 来做这个事情;
首先我们用 i 去与 5 对比,发现它等于 5,说明没有被其它线程改过,那我就把它设置为新的值 6,此次 CAS 成功,i的值被设置成了 6;
如果不等于 5,说明i被其它线程改过了(比如现在i的值为 2),那么我就什么也不做,此次 CAS 失败,i的值仍然为 2。
在这个例子中,i就是 V,5 就是 E,6 就是 N。

  • 那有没有可能我在判断了i为 5 之后,正准备更新它的新值的时候,被其它线程更改了i的值呢?

不会的。因为 CAS 是一种原子操作,它是一种系统原语,是一条 CPU 的原子指令,从 CPU 层面已经保证它的原子性。

当多个线程同时使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。

12.3 CAS的缺点

(1)ABA问题

如果一个线程t1正修改共享变量的值A,但还没修改,此时另一个线程t2获取到CPU时间片,将共享变量的值A修改为B,然后又修改为A,此时线程t1检查发现共享变量的值没有发生变化,但是实际上却变化了。

  • 解决办法: 使用版本号,在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A-B-A 就会变成1A-2B-3A。从Java1.5开始JUC包里提供了一个类AtomicStampedReference来解决ABA问题。AtomicStampedReference类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前版本号是否等于预期版本号,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
(2)循环时间长开销会比较大:

自旋重试时间,会给CPU带来非常大的执行开销

(3)只能保证一个共享变量的原子操作,不能保证同时对多个变量的原子性操作
  • 解决办法:
    从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,
    你可以把多个变量放在一个对象里来进行CAS操作

12.4 CAS使用注意事项

  • (1)CAS需要和volatile配合使用

CAS只能保证变量的原子性,不能保证变量的内存可见性。CAS获取共享变量的值时,需要和volatile配合使用,来保证共享变量的可见性

  • (2)CAS适用于并发量不高、多核CPU的情况

CPU多核情况下可以同时执行,如果不合适就失败。而并发量过高,会导致自旋重试耗费大量的CPU资源

13.HashMap和HashTable区别 难度系数:⭐

线程安全性不同
HashMap是线程不安全的,HashTable是线程安全的,其中的方法是Synchronized,在多线程并发的情况下,可以直接使用HashTable,但是使用HashMap时必须自己增加同步处理。

是否提供contains方法
HashMap只有containsValue和containsKey方法;HashTable有contains、containsKey和containsValue三个方法,其中contains和containsValue方法功能相同。

key和value是否允许null值
Hashtable中,key和value都不允许出现null值。HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。

数组初始化和扩容机制
HashTable在不指定容量的情况下的默认容量为11,而HashMap为16,Hashtable不要求底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂。

Hashtable扩容时,将容量变为原来的2倍加1,而HashMap扩容时,将容量变为原来的2倍。
————————————————

                        leader_song

原文链接:https://blog.csdn.net/leader_song/article/details/132094080

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

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

相关文章

Conmi遇到的坑——禅道的PCDN

好家伙&#xff0c;悄悄在后台吃了七十多G流量&#xff0c;我把你当兄弟宣传&#xff0c;你把我当PCDN吸。 还纳闷今天创建个VUE项目怎么提示D盘没空间&#xff0c;明明留了几十G&#xff0c;好家伙&#xff0c;一下子全吸干了。 删了两个&#xff0c;还有一个&#xff08;已…

吴恩达深度学习笔记:神经网络的编程基础2.5-2.8

目录 第一门课&#xff1a;神经网络和深度学习 (Neural Networks and Deep Learning)第二周&#xff1a;神经网络的编程基础 (Basics of Neural Network programming)2.5 导数&#xff08;Derivatives&#xff09; 第一门课&#xff1a;神经网络和深度学习 (Neural Networks an…

PNG图片合成,带手机外观设置,可自定义金额等

PNG图片合成&#xff0c;带手机外观设置&#xff0c;可自定义金额等 软件界面成品显示免责声明 软件界面 成品显示 免责声明 若因使用代码与官方造成不必要的纠纷&#xff0c;本人盖不负责&#xff0c;存粹技术爱好&#xff0c;若侵犯贵公司的权益&#xff0c;请告知&#xff…

美团2025春招第一次笔试题

第四题 题目描述 塔子哥拿到了一个大小为的数组&#xff0c;她希望删除一个区间后&#xff0c;使得剩余所有元素的乘积未尾至少有k个0。塔子哥想知道&#xff0c;一共有多少种不同的删除方案? 输入描述 第一行输入两个正整数 n,k 第二行输入n个正整数 a_i&#xff0c;代表…

教你用两种方式遍历循环python中的字典

开发中经常会用到对于字典、列表等数据的循环遍历&#xff0c;但是python中对于字典的遍历对于很多初学者来讲非常陌生&#xff0c;今天就来讲一下python中字典的循环遍历的两种方式。 注意&#xff1a; python2和python3中&#xff0c;下面两种方法都是通用的。 1. 只对键的…

电机特性学习

电机特性 电机堵转&#xff1a; 电机堵转的原理 玻璃升降器&#xff1a; 工作电压 升降器在 9V~16V 电压下应运行平稳,不允许有异音和卡滞现象。 工作电流 升降器的工作电流不大于 12A,堵转电流不大于 28A。 堵转 力 升降器 堵转 力应 不小于 212N。 玻璃升降器结构 电动车窗…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的日常场景下的人脸检测系统(附完整资源+PySide6界面+训练代码)

摘要&#xff1a;开发用于日常环境中的人脸识别系统对增强安全监测和提供定制化服务极为关键。本篇文章详细描述了运用深度学习技术开发人脸识别系统的全过程&#xff0c;并附上了完整的代码。该系统搭建在强大的YOLOv8算法之上&#xff0c;并通过与YOLOv7、YOLOv6、YOLOv5的性…

【数据结构】初识二叉搜索树(Binary Search Tree)

文章目录 1. 二叉搜索树的概念2. 二叉搜索树的操作1.1 二叉搜索树的查找1.2 二叉搜索树的插入1.3 二叉搜索树的删除 1. 二叉搜索树的概念 二叉搜索树又称二叉排序树&#xff0c;它可能是一棵空树&#xff0c;也可能是具有以下性质的二叉树&#xff1a; 若它的左子树不为空&am…

Python 导入Excel三维坐标数据 生成三维曲面地形图(体) 5-3、线条平滑曲面且可通过面观察柱体变化(三)

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…

掌握Redis,看完这篇文章就够了!

目录 1.Redis介绍 2.Redis服务器与客户端 3.Redis配置文件 4.Redis数据类型操作 4.1使用python连接数据库 4.2 字符串 4.3 哈希 4.4 键对应操作 4.5 列表 4.6 集合 4.7 有序集合 1.Redis介绍 Redis 是一个开源的内存数据库&#xff0c;它提供了一个高性能的 key-val…

【java数据结构】HashMap和HashSet

目录 一.认识哈希表&#xff1a; 1.1什么是哈希表&#xff1f; 1.2哈希表的表示&#xff1a; 1.3常见哈希函数&#xff1a; 二.认识HashMap和HashSet: 2.1关于Map.Entry的说明:,> 2.2Map常用方法说明&#xff1a; 2.3HashMap的使用案例&#xff1a; 2.4Set常见方法…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Slider)

滑动条组件&#xff0c;通常用于快速调节设置值&#xff0c;如音量调节、亮度调节等应用场景。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Slider(options?: SliderOption…

【算法面试题】-06

智能成绩表 题目描述 小明来到学校当老师&#xff0c;需要将学生按考试总分或单科分数进行排名&#xff0c;你能帮帮他吗&#xff1f; 输入描述 第 1 行输入两个整数&#xff0c;学生人数 n 和科目数量 m。 0 < n < 100 0 < m < 10 第 2 行输入 m 个科目名称&…

14双体系Java学习之数组

数组 ★小贴士 数组中保存固定数量的值&#xff0c;声明数组时需要制定数组中元素的类型&#xff0c;数组的长度在创建数组时设定。 保存数据的数据结构有很多&#xff0c;Java的标准函数库中就包含了许多复杂的数据结构&#xff0c;比如map、tree和set&#xff0c;以后会讲解的…

数据结构·复杂度

目录 1 时间复杂度 2 大O渐进表示法 举例子&#xff08;计算时间复杂度为多少&#xff09; 3 空间复杂度 前言&#xff1a;复杂度分为时间复杂度和空间复杂度&#xff0c;两者是不同维度的&#xff0c;所以比较不会放在一起比较&#xff0c;但是时间复杂度和空间复杂度是用…

Ubuntu 安装腾讯会议

1.官网下载 进入腾讯会议下载官网下载腾讯会议Linux客户端 选择x86_64格式安装包下载 若不知道自己的系统架构,输入 uname -a 在命令行结果中查看系统架构信息 2.终端命令安装 cd {你的下载路径} sudo dpkg -i TencentMeeting_0300000000_3.19.0.401_x86_64_default.publi…

数据结构·二叉树(一)

1. 树概念及结构 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;树叶朝下的。 有一个特殊的节点&#xff0c;称为根节点&#…

嵌入式系统工程师错题总结

笔者来介绍一下嵌入式系统工程师考试的一些易错题目 题目介绍  流水线指令计算公式&#xff1a;一条指令总时间max&#xff08;单个指令执行时间&#xff09;*&#xff08;指令数-1&#xff09;  平均故障间隔时间  ICMP协议&#xff1a;传送通信问题相关的消息。 …

sqlserver中将csv非空间数据(带点坐标)转为空间数据

1、导入csv数据 2、修改字段shape为空间字段 ALTER TABLE FJPOIHB66 ALTER COLUMN shape geometry;3、空间字段转字符串 UPDATE FJPOIHB66 SET shape geometry::STGeomFromText(CONVERT(nvarchar(254),shape), 4326);4、设置主键字段 5、即可

141 Linux 系统编程18,线程,ps –Lf 进程 查看LWP,线程间共享数据,优缺点,编译加-lpthread,

一 线程概念 什么是线程 LWP&#xff1a;light weight process 轻量级的进程&#xff0c;本质仍是进程(在Linux环境下) 进程&#xff1a;独立地址空间&#xff0c;拥有PCB 线程&#xff1a;有独立的PCB&#xff0c;但没有独立的地址空间(共享) 区别&#xff1a;在于是否共…