前言:
本篇文章主要讲解面试中经常问到的线程安全的集合类的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。
如果文章有什么需要改进的地方欢迎大佬提出,对大佬有帮助希望可以支持下哦~
小威在此先感谢各位小伙伴儿了😁
以下正文开始
文章目录
- 线程安全的类,集合,关键字总结
- 线程安全类
- 线程安全集合
- 线程安全方法
- StringBuffer
- Hashtable
线程安全的类,集合,关键字总结
Java中的部分类,集合,关键字都能保证线程安全,接下来我们从线程安全的类,集合,关键字详细介绍:
线程安全类
线程安全类:线程安全类是一些经过设计,能够在多线程环境下安全地使用的类,它们大多数都是通过内部实现同步机制保证正常工作。线程安全类主要有以下几种:
- StringBuffer:线程安全的字符串缓冲区,所有对于它的操作都是同步的。
- Vector:线程安全的数组序列,支持对其中的元素进行增删查改等操作。
- Hashtable:线程安全的哈希表,也就是键值对映射(Map),所有对于它的操作都是同步的。
- ConcurrentHashMap:线程安全的哈希表,采用了分段锁的方式,不同线程可以同时对不同的分段进行操作。
线程安全集合
线程安全集合:线程安全集合是能够在线程安全的前提下支持快速地添加、查询、删除其中元素的集合。Java中线程安全的集合主要有以下几种:
- CopyOnWriteArrayList:线程安全的动态数组,它对于写操作会复制当前数组,修改完后再将它赋值给原引用,因此读操作和写操作可以同时进行而不需要加锁。
- CopyOnWriteArraySet:线程安全的集合,它采用了CopyOnWriteArrayList来实现,保证了元素唯一、有序,并且在读操作的同时也可以进行写操作。
- ConcurrentLinkedQueue:线程安全的队列,它采用了CAS(Compare And Swap)算法来保证并发操作时的原子性。
线程安全方法
线程安全方法:Java中也提供了许多线程安全方法,这些方法在多线程环境下能够保证数据的正确性,主要有以下几种:
- synchronized关键字:能够锁住当前代码块或方法,保证同一时间只能有一个线程进入执行该代码块或方法。
- volatile关键字:保证变量的可见性以及禁止指令重排序,从而保证多线程情况下变量的正确性。
- Atomic类:提供了一组原子操作方法,使用CAS算法保证操作的原子性。
- Lock接口及其实现类:提供了更加灵活的锁机制,支持不可重入锁、可重入锁以及读写锁等。
- ThreadLocal类:为每个线程提供了独立的变量副本,从而避免了线程安全问题。
StringBuffer
StringBuffer是Java中用于字符串操作的可变类,它在多线程环境下通过使用synchronized关键字来保证线程安全。
-
方法级别的同步:StringBuffer的方法(如append(), delete(), replace()等)都使用synchronized关键字进行同步。这意味着每个方法在执行时会获取一个对当前StringBuffer实例的锁,防止其他线程同时访问和修改对象。
-
可变性:StringBuffer是可变类,允许在原始字符串上进行修改操作。而不像String类那样是不可变的。这可以避免在多线程环境中创建大量的临时字符串对象,减少内存开销。
-
线程安全的追加操作:StringBuffer的append()方法是线程安全的,因为它在内部使用了同步机制。多个线程可以并发调用append()方法,确保结果正确。
尽管StringBuffer提供了线程安全的操作,但在现代的Java应用程序中,通常更倾向于使用StringBuilder类。与StringBuffer相比,StringBuilder没有同步机制,因此更高效。如果不需要在多线程环境中共享可变字符串对象,建议使用StringBuilder。如果确实需要在多线程环境中进行字符串操作,可以使用StringBuffer来保证线程安全。
Hashtable
Hashtable是Java中的一种哈希表实现,它基于数组和链表结构实现。下面介绍Hashtable的底层原理:
内部存储结构
Hashtable内部使用一个数组来保存元素,每个数组元素称为桶(bucket)。通过将键值哈希后得到索引值,在对应的桶中存储相应的值。如果多个键值映射到同一个索引上,就会在这个索引的桶上形成一个链表。
哈希函数
Hashtable使用哈希函数将键转换成整数索引,从而快速定位桶。Java默认提供了一种简单的哈希函数:hashCode()方法,可以生成32位整数类型的哈希码。
解决哈希冲突
由于不同键可能映射到相同的索引位置上,所以必须要解决哈希冲突问题。通常有两种解决办法:拉链法(Chaining)和线性探测(Linear Probing)。
拉链法:当发生哈希冲突时,在当前位置建立一个链表来存储所有键值映射到该位置上的数据。
线性探测:当发生哈希冲突时,在当前位置往后寻找下一个空槽并插入数据。
Hashtable采用了拉链法来解决哈希冲突。当多个键值映射到同一个桶上时,Hashtable会在这个桶上建立一个链表,将不同的键值对存储在链表中。
扩容
为了避免哈希冲突导致性能下降,Hashtable采用了动态扩容机制。当元素数量超过负载因子(load factor)时,默认0.75,Hashtable会自动进行扩容操作,并重新分配桶数组大小和哈希码。
线程安全
Hashtable是线程安全的,在多线程环境下可以并发访问,但是效率相对较低。因为它采用了synchronized关键字来保证线程安全。
Hashtable小结:
-
方法级别的同步:Hashtable的方法(如put(), get(), remove()等)都使用synchronized关键字进行同步。这意味着每个方法在执行时会获取一个对当前Hashtable实例的锁,阻止其他线程同时访问和修改对象。
-
互斥访问:由于synchronized关键字的存在,一次只能有一个线程访问Hashtable的方法。这种互斥访问确保了线程之间的安全性,并防止并发修改导致数据不一致的问题。
-
内部同步机制:Hashtable使用内部锁来保护其状态。当一个线程正在执行一个方法时,其他线程会被阻塞,直到该线程完成操作。
-
原子操作:Hashtable的方法在执行时是原子的,即一个方法的执行是不可分割的。这确保了操作的完整性,防止多个线程同时对同一个键值对进行操作。
文章到这里就先结束了,感兴趣的可以订阅专栏哈,后续会继续分享相关的知识点。