moka实习生一面0607

java支持哪些数据类型

byte、short、int、long、boolean、char、float、double

int取值范围-

2^31~2^31-1

什么是包装类

基础数据类型相应的对象,用于需要对象的地方使用这些基本数据类型。

Integer i=null;int j=i;会怎样,会报什么错误

会报java.lang.NullPointerException,自动拆箱时调用intValue()方法,为空则报错

list和set区别,set怎么保证唯一。

一个顺序数组,一个不重复集合

  1. HashSet
    • HashSet基于HashMap实现。它使用哈希表来存储元素。每个元素通过其hashCode()方法的返回值来确定其存储位置(即哈希码对应的桶),如果该桶内已经存在相同哈希码的元素,则通过equals()方法进一步判断是否为同一个元素。如果equals()方法返回true,则认为两个元素相同,此时新元素不会被添加到集合中,从而保证了元素的唯一性。
  2. LinkedHashSet
    • LinkedHashSetHashSet的一个子类,它在HashSet的基础上,通过维护一个运行于所有条目的双向链表来记录插入顺序。但它同样是通过哈希表和equals()/hashCode()方法来保证元素的唯一性。与HashSet不同的是,它保证了元素的有序性(插入顺序)。
  3. TreeSet
    • TreeSet基于TreeMap实现。它使用红黑树这种数据结构来存储元素,并保证集合元素处于排序状态。TreeSet在添加元素时,会先判断该元素是否已经存在于集合中,这是通过元素的自然排序或构造TreeSet时提供的Comparator来进行比较的。如果集合中已存在该元素(即比较结果为相等),则不会添加新的元素,从而保证了集合中元素的唯一性。

arraylist和linkedlist区别,arraylist动态数组怎么实现的

  • ArrayList:基于数组实现的动态数组。当数组容量不足以容纳新元素时,ArrayList会自动进行扩容操作,通常会创建一个新的数组,并将原数组中的元素复制到新数组中。这种实现方式使得ArrayList在内存中是连续存储的,每个元素占用固定大小的内存空间。
  • LinkedList:基于双向链表实现。每个节点都包含数据部分和指向前一个节点及后一个节点的引用。这种结构使得LinkedList在插入和删除元素时,只需要修改相邻节点的指针,而不需要移动其他元素。

hashmap的put操作,除了拉链法呢

  1. 计算哈希值:首先,使用键的哈希码(通过调用 key.hashCode())计算哈希值。
  2. 确定桶的位置:通过哈希值与 HashMap 容量减一(capacity - 1)进行按位与操作(&),得到一个索引值,这个索引值对应于数组中的一个桶(bucket)的位置。
  3. 处理哈希冲突
    • 如果桶中没有元素(即,null),则将新的键值对存储在桶中。
    • 如果桶已经包含一个或多个元素(即,链表或红黑树),则遍历该链表或树以查找键是否已经存在:
      • 如果键已存在,则更新该键对应的值,并返回旧值。
      • 如果键不存在,则将新的键值对添加到链表或树的末尾(注意,在某些情况下,如果链表变得太长,它会转换为红黑树以提高搜索效率)。
  4. 调整大小(可选):如果添加新元素后,HashMap 的大小超过了其加载因子(load factor)与当前容量的乘积,则进行扩容,以容纳更多的元素。扩容涉及创建一个新的更大的数组,并将现有元素重新哈希并移动到新数组中。

还有线性探测法、再哈希法等。

hashmap是线程安全的吗

不是

synchronized加在方法上和加在代码块上有什么区别吗,那一个静态方法和一个非静态方法它们的锁对象是一样的吗,哪不一样

        方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词), 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。

        代码块的同步是利用monitorenter和monitorexit这两个字节码指令。它们分别位于同步代码块的开始和结束位置。当jvm执行到monitorenter指令时,当前线程试图获取monitor对象的所有权,如果未加锁或者已经被当前线程所持有,就把锁的计数器+1;当执行monitorexit指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。如果获取monitor对象失败,该线程则会进入阻塞状态,直到其他线程释放锁。

  • 静态方法:synchronized修饰的静态方法锁定的是当前类的Class对象,也称为类锁。这意味着所有实例共享同一个锁,当一个线程访问某个类的静态synchronized方法时,其他线程不能访问该类的任何其他静态synchronized方法,直到该线程完成对该方法的访问。
  • 非静态方法:synchronized修饰的非静态方法锁定的是调用该方法的对象实例,也称为对象锁。不同的实例对象调用非静态synchronized方法时,它们之间不存在竞争关系,因为每个实例都有自己的锁。

除了synchronized还用过哪些锁,还了解哪些锁

记录锁、间隙锁和临键锁、读写锁、行锁、表锁、意向锁、volatile、atomic、ReetrantLock、乐观锁、悲观锁、偏向锁、轻量级锁

什么是间隙锁

间隙锁锁定的是一个范围(左开右开),但不包括记录本身。它主要用于防止幻读(Phantom Reads),即在一个事务内读取到的某范围内的记录数,在另一个并发事务插入新记录到该范围内后,再读取得到的记录数不同。

当使用普通索引时,无论等值查询还是范围查询,都生成间隙锁

使用唯一索引时,等值未命中或范围查询生成间隙锁

分布式锁有用过吗,什么场景下用的,

用过,抢购时防止超卖时配合限流使用

redis怎么实现分布式锁

redis实现分布式锁的依靠redis命令是单线程执行的,保证分布式锁的原子性。

分布式锁有以下几种方式:

setnx

Boolean ifAbsent = cache.opsForValue().setIfAbsent(key,value); //业务代码 cache.delete(key);

如果线程 1 在成功获取锁后,执行流程时异常结束,没有执行释放锁操作,这样就会不会释放锁

如果方法执行异常导致的线程被回收,那么可以将解锁操作放到 finally 块中。

但是还有存在死锁问题,如果获得锁的线程在执行中,服务被强制停止或服务器宕机,锁依然不会得到释放

setnx+expire

Boolean ifAbsent = cache.opsForValue().setIfAbsent(key,value); cache.expire(key,TIME,TimeUnit.SECONDS); //业务代码 cache.delete(key);

虽然对分布式锁添加了过期时间,但依然无法避免极端情况下的死锁问题。那就是如果在客户端加锁成功后,还没有设置过期时间时宕机

如果想要避免添加锁时死锁,那就对添加锁标志位 & 添加过期时间命令 保证一个原子性,要么一起成功,要么一起失败

set

SET key value [expiration EX seconds|PX milliseconds] [NX|XX]

  • EX: 设置超时时间,单位是秒。

  • PX: 设置超时时间,单位是毫秒。

  • NX: IF NOT EXIST 的缩写,只有 KEY 不存在的前提下 才会设置值。

  • XX: IF EXIST 的缩写,只有在 KEY 存在的前提下 才会设置值。

为了防止误删其他线程的锁:添加锁标志位时,同时为每个客户端设置了 uuid 作为锁标志位的 val,解锁时需要判断锁的 val 是否和自己客户端的相同,辨别成功才会释放锁。我们可以将解锁操作放到 finally 块。这样执行业务逻辑如果抛出异常时可以直接释放锁。

解锁时, 由于判断锁和删除标志位并不是原子性的,所以可能还是会存在误删

解决这种非原子操作的方式只能 将判断元素值和删除标志位当作一个原子操作

lua

使用 Lua 脚本有什么好处呢?

  1. 减少网络开销:原本我们需要向 Redis 服务请求多次命令,可以将命令写在 Lua 脚本中,这样执行只会发起一次网络请求。

  2. 原子操作:Redis 会将 Lua 脚本中的命令当作一个整体执行,中间不会插入其它命令。

  3. 复用:客户端发送的脚本会存储 Redis 中,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。

Redission

  • 保证了加锁、解锁之间的原子性。

  • 可重入的分布式锁。

  • 分布式锁自动延期功能。

用setnx设置锁会有什么问题

问题是误删其他线程的锁

什么是分布式事务

分布式事务是指由多个节点参与的分布式系统中的一组操作,这些操作要么全部成功,要么全部失败。它主要用于解决在分布式环境下,由于节点的独立性和异构性所带来的数据一致性和事务原子性的挑战。

java里面开启单机事务可以用@Transactional,它是怎么控制事务的。

在Java中,@Transactional 注解是Spring框架提供的一个非常强大的声明式事务管理功能。它允许开发者通过简单的注解方式,而非侵入式地编程来管理事务。@Transactional 注解可以应用于接口、类或方法上,以声明性地指定事务的边界和事务属性。

@Transactional 如何控制事务

  1. 声明事务边界
    @Transactional注解被应用于一个方法上时,它声明了该方法的边界为事务的边界。这意味着,从方法开始执行到方法执行结束(无论是正常结束还是异常结束),都将被视为一个完整的事务。Spring会在方法调用前后自动开启和关闭事务。

  2. 事务属性
    @Transactional 注解还允许你指定事务的各种属性,如传播行为(Propagation)、隔离级别(Isolation)、超时时间(Timeout)、只读标志(ReadOnly)以及回滚规则(RollbackFor、NoRollbackFor)等。这些属性可以进一步控制事务的行为。

  3. 代理机制
    Spring使用AOP(面向切面编程)技术来实现@Transactional注解的功能。具体来说,Spring会为被@Transactional注解的类或方法创建一个代理对象。当调用这些类或方法时,实际上是在调用代理对象。代理对象会在调用实际方法之前和之后,分别执行开启事务和关闭事务(或回滚事务)的操作。

@Transactional事务传播机制

在TransactionDefinition接口中定义了七个事务传播行为:

  1. PROPAGATION_REQUIRED(默认):

    1. 如果当前存在事务,则加入该事务;

    2. 如果当前没有事务,则创建一个新的事务。

    3. 如果嵌套调用的两个方法都加了事务注解,并且运行在相同线程中,则这两个方法使用相同的事务中。如果运行在不同线程中,则会开启新的事务。

  2. PROPAGATION_SUPPORTS:

    1. 如果当前存在事务,则加入该事务;

    2. 如果当前没有事务,则以非事务方式执行。

  3. PROPAGATION_MANDATORY:

    1. 如果当前存在事务,则加入该事务;

    2. 如果当前没有事务,则抛出异常IllegalTransactionStateException。

  4. PROPAGATION_REQUIRES_NEW:

    1. 创建一个新的事务,并挂起当前存在的事务(如果有)。需要使用JtaTransactionManager作为事务管理器。

    2. 这意味着被调用的方法将在一个全新的事务中运行,与当前事务无关。

  5. PROPAGATION_NOT_SUPPORTED:

    1. 总是非事务地执行,并挂起任何存在的事务。需要使用JtaTransactionManager作为事务管理器。

  6. PROPAGATION_NEVER:

    1. 总是非事务地执行,如果存在一个活动事务,则抛出异常。

  7. PROPAGATION_NESTED:

    1. 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行。

    2. 如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务, 则按PROPAGATION_REQUIRED 属性执行。

Thread的start方法和run方法的区别

1. start() 方法

  • 作用start() 方法用于启动线程,使该线程开始执行。当调用线程的 start() 方法时,Java 虚拟机(JVM)会为该线程分配必要的资源,并调用该线程的 run() 方法。每个线程都是从其 run() 方法开始执行的。
  • 特点
    • start() 方法是同步的,它确保线程不会重复启动。
    • start() 方法调用后,线程的状态将从 NEW(新建)变为 RUNNABLE(可运行),然后线程调度器将决定何时以及是否实际执行该线程的 run() 方法。
    • start() 方法只能被调用一次。如果尝试再次调用,则会抛出 IllegalThreadStateException 异常。

2. run() 方法

  • 作用run() 方法是线程的主体,包含了线程要执行的代码。当线程启动时,JVM 会自动调用该线程的 run() 方法。但是,如果你直接调用一个线程的 run() 方法(而不是通过 start() 方法),那么该方法的执行将不会在新的线程中执行,而是在当前线程中同步执行。
  • 特点
    • run() 方法是 Thread 类的一个普通方法,可以被覆盖(Override)以提供自定义的线程执行体。
    • 直接调用 run() 方法并不会启动新线程,它仅仅是在当前线程中同步执行 run() 方法中的代码。
    • run() 方法可以被多次调用,但每次调用都是在当前线程中执行,除非你在 run() 方法内部创建了新的线程。

数据库的事务有什么特性

A:事务是原子性的,不存在中间状态,要么成功要么失败

C:事务是一致性的,事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

I:事务是隔离性的,事务并行感受不到其他事务的存在。

D:事务是持久性的,事务提交后的改变是永久的。

Mysql怎么实现事务,比如怎么实现隔离性

隔离性通过MVCC机制保证。

一般什么情况下加索引

经常排序、分组、查询、联合的字段

多个字段都有建索引,考虑联合索引

联合索引有什么特性

最左优先匹配

Redis都支持哪些数据类型,你用过哪个,都是什么场景下用到的

String、Hash、List、Set、Zset、GEO、HyperLogLog、Bitmaps、Streams

使用场景:

  • 字符串(String)
    • 描述:字符串是 Redis 最基础的数据类型,用于存储文本或二进制数据。
    • 应用场景
      • 缓存:Redis 常被用作数据缓存层,存储热点数据,如用户信息、商品详情等,以减少对数据库的访问压力。
      • 计数器:实现快速计数的功能,如视频播放次数、网站访问量等。
      • 共享 Session:将用户的 Session 数据存储在 Redis 中,实现跨多个服务器的 Session 共享。
  • 哈希(Hash)
    • 描述:哈希是一种键值对集合,其中每个键都可以映射到一个或多个字段和值。
    • 应用场景
      • 用户信息管理:存储用户信息,如用户名、密码、邮箱等,方便快速访问和修改。
      • 商品详情管理:存储商品的详细信息,如名称、价格、库存等。
  • 列表(List)
    • 描述:列表是一种基于字符串的线性表数据结构,可以存储多个有序的字符串元素。
    • 应用场景
      • 消息队列:通过 LPUSH 和 RPOP 实现队列数据结构,用于任务调度、消息传递等。
      • 时间线:在社交网络中实现用户的时间线功能,存储和展示用户发布的内容。
  • 集合(Set)
    • 描述:集合是一种无序的字符串集合,其中的每个元素都是唯一的,且没有重复的元素。
    • 应用场景
      • 用户标签管理:存储用户的兴趣标签,用于推荐系统或内容过滤。
      • 黑名单管理:存储需要限制访问的用户或 IP 地址。
  • 有序集合(Sorted Set)
    • 描述:有序集合是一种特殊的集合,其中的每个元素都会关联一个分数,通过分数可以对集合进行排序。
    • 应用场景
      • 排行榜:实现各种排行榜功能,如游戏排行榜、销售排行榜等。
      • 权重调度:根据元素的权重进行排序和选择,如任务调度、负载均衡等。
  • 流(Streams)
    • 描述:用于存储无限量的消息序列,支持消息追加、读取和修剪。
    • 应用场景:实现消息队列的高级功能,如消息持久化、消息确认、消费者组等。
  • 位图(Bitmaps)
    • 描述:紧凑地表示大量二进制数据的集合,每位代表一个元素的存在性。
    • 应用场景:用于实现高效的位运算操作,如用户在线状态统计、布隆过滤器等。
  • 地理空间(Geospatial)
    • 描述:存储地理位置数据,如经度和纬度,支持地理查询,如查找附近的点或计算距离。
    • 应用场景:实现基于地理位置的服务,如地图应用、位置搜索等。
  • HyperLogLog
    • 描述:近似计算集合中唯一元素数量的高效数据结构。
    • 应用场景:用于估计大量数据集中的唯一元素数,如用户UV统计等。

学过哪些数据结构

数组、链表、字符串、堆、优先队列、队列、栈、集合、哈希表、线段树、树状数组、b树和b+树、跳表、二叉树以及AVL和二叉搜索树,单调栈、邻接表和邻接图,并查集

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

装B且挨揍の

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值