经典面试题3(ThreadLocal ,同步锁,死锁,乐观锁,悲观锁,反射,Hashmap 和 hashtable ConcurrentHashMap)

目录

1,ThreadLocal 的原理

2,同步锁,死锁,乐观锁,悲观锁

3,反射

4,Hashmap 和 hashtable ConcurrentHashMap 区别


1,ThreadLocal 的原理

ThreadLocal 是线程本地变量,它的作用就是,对于同一个变量,在不同的线程之中,可以获取到不同的值。

set,get,remove

它的值保存在线程本地的,保证线程的安全性,他有一个静态内部类ThreadLocalMap,也就是key-value形式的存储值。ThreadLocal 本身并不存储值,它只是作为一个 key 保存到 ThreadLocalMap 中。

它的 key 用的是弱引用,因为没有强引用链,弱引用在 GC 的时候可能会被回收。这样就会在 ThreadLocalMap 中存在一些 key 为 null 的键值对(Entry)。因为 key 变成 null 了,我们是没法访问这些 Entry 的,但是这些 Entry 本身是不会被清除的。如果没有手动删除对应 key 就会导致这块内存即不会回收也无法访问,也就是内存泄漏。

于是,当我们确定不再使用某一个变量时,我们就需要使用remove方法,手动移除线程中的对应的key-value,避免出现内存泄漏的问题。

2,同步锁,死锁,乐观锁,悲观锁

再单线程情况下,不存在资源的竞争,不用锁。再多线程情况下,因为存在资源的共享与竞争,为了合理的分配资源,所以我们需要锁。

同步锁:

当多个线程同时访问同一个数据时,很容易出现问题。为了避免这种情况出现,我们要保证线程同步互斥,就是指并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。

使用监视器实现,每一个Java对象都关联着一个监视器,在多线程的环境下,当一个线程需要使用一个Java对象的同步锁机制的时候,都需要锁住这个对象对应的监视器,当不需要的时候,需要解锁这个对象的监视器,同时,一次只能有一个线程允许锁住该对象对应的监视器,其他的线程只能阻塞,等待解锁。

死锁:

产生原因:

在多线程的情况下,一个线程在进行,其余的线程同时阻塞的时候,他们中的一个或者或者全部都在等待某个资源被释放的情况就是死锁。

  • 资源竞争:系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在 运行过程中,会因争夺资源而陷入僵局。
  • 进程推进顺序非法:进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。

避免死锁:

1,加锁顺序:线程按照一定的顺序加锁

2,加锁时限,先是尝试获取锁的时候一定要加上一定时限,超过时限则放弃对该锁的请求,并且释放自己占有的锁

3,死锁检测

检测到死锁时,可以释放掉所有的锁,回退,并且等待一段随机时间重试。但是如果又大量的线程竞争同一批锁,他们还是会重复的死锁。更好的方案是给这些线程设置优先级,让一个或者多个线程回退,剩下的线程就还是保持着他们需要的锁。

乐观锁:

乐观锁就是持比较乐观态度的锁。在操作数据时非常乐观,认为别的线程不会同时修改数据,所以不会上锁。就但是在更新的时候会判断在此期间别的线程有没有更新过这个数据。

适用于写少读多的情况,减少操作冲突,可以省去锁竞争的开销,提高系统的吞吐量。

可以使用版本号机制和CAS算法实现,类似数据库的write_conditio 机制, Java 中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。

悲观锁:

悲观锁就是持悲观态度的锁。就在操作数据时比较悲观,每次去拿数据的时候认为别的线程也会同时修改数据,所以每次在拿数据的时候都会上锁,这样别的线程想拿到这个数据就会阻塞直到它拿到锁。

适用于写多读少的情况,如果这种情况还使用乐观锁就会出现操作冲突,导致应用层不断地Retry,降低系统的性能。

传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java 中synchronized 和 ReentrantLock 等独占锁就是悲观锁思想的实现。

3,反射

概念:

        在 Java 中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象法的功能为 Java 语言的反射机制。

作用:

通过使用java语言的反射机制可以操作字节码文件(读取和修改)

提高反射机制可以操作代码片段(class文件)

  1. 获取Class的三种方式:
  2. 想要操作一个字节码,就先要获取到,有三种方式
  3. 1,静态方法:Class.forName("类的全路径"),最常用,最安全,性能最好的方法。
  4. 2,对象.getClass()
  5. Person p=new Person(); 
    Class clazz=p.getClass(); 

    3,属性.class

  6. Class clazz=Person.class; 

    最常用的就是Spring框架其中的Bean,我们不用自己new对象,直接从容器之中的Beans获取,然后通过getBean获取。

  7. 4,Hashmap 和 hashtable ConcurrentHashMap 区别

之前我们了解了Hash的原理,知道Hashmap和Hashtable和ConcurrentHashMap都是集合类Map的实现类。区分一下,大概为三个问题:

(HashMap 和 HashTable 区别):

1,HashMap时非线程安全的,HashTable时线程安全的。

2,HashMap的key-value都是允许null存在的,HashTable不允许。

3,HashMap效率高于HashTable。

4,HashTable是同步的,适合于单线程,HashMap不是同步,适合多线程。

总结,一般不建议使用HashTable,因为HashTable时遗留类,内部又很多没有优化和冗余。即使再多线程的环境HashMap线程不安全也可以使用ConcurrentHashMap来替代,也不会至于使用HashTable。

(HashMap 和 ConcurrentHashMap 区别):

1, ConcurrentHashMap是HashMap的升级版,HashMap是线程不安全的,而ConcurrentHashMap是线程安全。而其他功能和实现原理和HashMap类似。

2,同样都是再1.8加入了红黑树。

3,ConcurrentHashMap采用锁分段技术,将整个Hash桶进行了分段segment,也就是将这个大的数组分成了几个小的片段segment,而且每个小的片段segment上面都有锁存在,那么在插入元素的时候就需要先找到应该插入到哪一个片段segment,然后再在这个片段上面进行插入,而且这里还需要获取segment锁。

(HashTable 和 ConcurrentHashMap 区别)

1,HashTable 使用的是 Synchronized 关键字修饰,ConcurrentHashMap 是 JDK1.7 使用了锁分段技术来保证线程安全的。JDK1.8ConcurrentHashMap 取消了Segment 分段锁,采用 CAS 和 synchronized 来保证并发安全。数据结构跟 HashMap1.8的结构类似,数组+链表/红黑二叉树。

2,synchronized 只锁定当前链表或红黑二叉树的首节点,这样只要 hash 不冲突,就不会产生并发,效率又提升 N 倍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不会Java的MING

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

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

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

打赏作者

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

抵扣说明:

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

余额充值