线程安全的集合类

java.util.Hashtable

Hashtable继承实现关系图

  • (1)Hashtable是一个散列表,它存储的内容是键值对(key-value)映射
  • (2)Hashtable继承Dictionary,实现Map、Cloneable、java.io.Serializable接口。
  • (3)Hashtable的实例有两个参数影响其性能:初始容量和加载因子。
    • 容量是哈希表中桶的数量,初始容量是哈希表创建时的容量
    • 在发生“哈希冲突”时,单个桶会存储多个条目,这些条目按顺序搜索。
    • 加载因子是对哈希表在其容量自动增加之前可以达到多满的一个尺度。默认加载因子是0.75。
  • Hashtable的函数都是同步的,它是线程安全的,但它的key、value都不可用为null,Hashtable中的映射不是有序的。
  • Hashtable提供的几个主要方法,包括get()、put()、remove()等,都是synchronized修饰的,不会出现两个线程同时对数据进行操作的情况,保证了线程安全
  • Hashtable实现的Cloneable接口,即实现了clone()方法,clone()方法就是克隆一个Hashtable对象并返回。Hashtable实现的Serializable接口实现类串行读取、写入功能。
    Hashtable示例:
 public static void main(String[] args){
      Hashtable<String, Integer> numbers = new Hashtable<>();
      numbers.put("one", 1);
      numbers.put("two", 2);
      numbers.put("three", 3);
      Integer n = numbers.get("two");
      if (n != null){
          System.out.println("two = " + n);
      }
  }

运行结果:

two = 2

java.util.concurrent.ConcurrentHashMap

在这里插入图片描述

  • (1)ConcurrentHashMap继承与AbstractMap,实现了Map、java.io.Serializable接口。
  • ConcurrentHashMap是HashMap的线程安全版,同Hashtable相比,ConcurrentHashMap不仅保证了访问的线程安全性,而且在效率上也有很大的提高
  • ConcurrentHashMap允许多个修改操作并发运行,关键在于使用了锁分离技术,即代码块锁而不是方法锁。它使用了多个锁来控制对hash表的不同部分进行修改。
  • ConcurrentHashMap内部使用段(Segment)来表示不同的而部分,每个段就是一个小的hash table,它们有自己的锁(有ReentrantLock实现),只要多个修改操作发生在不同段上,就可以并发运行。
      static class Segment<K,V> extends ReentrantLock implements Serializable {}
    
  • ConcurrentHashMap实现的Serializable接口实现类串行读取、写入功能。
    ConcurrentHashMap示例:
     public static void main(String[] args){
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
        map.put("one", 1);
        map.put("two", 2);
        map.put("three", 3);
        System.out.println(map.get("two"));
    
        if (map.containsKey("one") && map.get("one").equals(1)){
            map.remove("one");
        }
    }
    
    运行结果:
    2
    

CopyOnWrite机制介绍

  • CopyOnWrite容器是写时复制的容器。在往容器添加元素时,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素后,再讲原容器的引用指向新的容器。
  • CopyOnWrite容器可以进行并发地读而不需要加锁。添加元素的时候需要加锁ReentranLock
  • CopyOnWrite容器是读写分离的思想,读和写不同的容器
  • CopyOnWrite的应用场景:
    • CopyOnWrite并发容器用于读多写少的并发场景。如白名单、黑名单、商品类目的访问和更新等场景。
  • CopyOnWrite的缺点:
    • 内存占用问题:因为CopyOnWrite的写时复制机制,在进行写操作的时候,内存里会同时有两个对象的内存。解决方法是通过压缩容器中的元素减少大对象的内存消耗
    • 数据一致性问题: CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。

java.util.concurrent.CopyOnWriteArrayList

在这里插入图片描述

  • CopyOnWriteArrayList中的set、add、remove等方法,都使用了ReentrantLock进行加锁,unlock()解锁
  • CopyOnWriteArrayList在add()方法中,使用Arrays.copyOf()来拷贝副本,在副本上增加元素,然后改变原引用指向副本。
  • CopyOnWriteArrayList的读操作不加锁,写操作类实现对其进行了加锁
    public E set(int index, E element) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        ...
    }
    
  • CopyOnWriteArrayList类是一个线程安全的List接口的实现,适合运用在读操作远远多于写操作的应用中,特别在并发情况下,可以提供高性能的并发读取,并且保证读取的内容一定是正确的,不受多线程并发问题影响
    示例:
public static void main(String[] args){
       CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
       list.add("one");
       list.add("two");
       list.add(1,"three");//list下标从零开始
       System.out.println(list.get(1));

       if (list.contains("three")){
           Iterator<String> value = list.iterator();
           while (value.hasNext()){
               System.out.println(value.next());
           }

       }
   }

运行结果:

three
one
three
two

java.util.concurrent.CopyOnWriteArraySet

在这里插入图片描述

  • CopyOnWriteArraySet是在CopyOnWriteArrayList的基础上,使用了java的装饰模式。如存储介质使用了CopyOnWriteArrayList存储数据,remove方法调用CopyOnWriteArrayList的remove方法,add方法调用CopyOnWriteArrayList的addIfAbsent方法。
  • CopyOnWriteArrayList的实现原理适用CopyOnWriteArraySet。
  • java里面List和Set的相同和不同之处适用于CopyOnWriteArrayList和CopyOnWriteArraySet,后两者都是线程安全机制。
    示例:
public static void main(String[] args){
      CopyOnWriteArraySet<String> list = new CopyOnWriteArraySet<>();
      list.add("one");
      list.add("two");


      if (list.contains("two")){
          Iterator<String> value = list.iterator();
          while (value.hasNext()){
              System.out.println(value.next());
          }

      }
  }

运行结果:

one
two

Vector

Vector

  • Vector是矢量队列,通过数组保存数据。它继承了AbstractList,实现了RandomAccess、Cloneable、Serializable等接口。
  • Vector继承了AbstractList,实现了List,Vector是一个队列,支持相关的添加、删除、修改、遍历等操作
  • Vector实现了RandomAccess接口,实现随机访问功能。RandomAccess是Java中用来被List实现,为List提供快速访问功能的。在Vector中,可以通过元素的序号快速获取元素对象,即快速随机访问
  • Vector实现了Cloneable接口,实现克隆功能。
  • Vector的操作是线程安全的,利用Synchronized同步锁的方法实现线程安全。addElement()、removeElement()、get()等方法都是synchronized修饰。
    示例:
public static void main(String[] args){
      Vector<String> list = new Vector<>();
      list.addElement("one");
      list.addElement("two");
      list.addElement("three");
      list.removeElement("two");

      if (list.contains("three")){
          Iterator<String> iterator = list.iterator();
          while (iterator.hasNext()){
              System.out.println(iterator.next());
          }
      }
  }

运行结果:

one
three

StringBuffer和StringBuilder

  • StringBuffer是线程安全的,方法加synchronized修饰,StringBuilder不是线程安全的。
  • 在高并发的情况下,如果不需要考虑数据安全的问题时,应尽量选择StringBuffer,由于没有资源等待的情况,执行效率和性能会高很多。 StringBuffer源码
    StringBuilder源码展示
除了提供高性能的线程安全集合类,java的并发包还提供了以下功能: 1. 并发执行框架:java.util.concurrent包中的Executor框架提供了一种使用线程池的方式来执行并发任务。它将任务的提交与任务的执行解耦,可以通过线程池来管理和复用线程,并提供了任务执行的调度和控制的功能。 2. 并发工具类:并发包中还提供了一些工具类,如CountDownLatch、CyclicBarrier、Semaphore等,用于帮助实现更加复杂的并发控制。这些工具类可以用于同步多个线程的执行,控制线程的并发数量,并在一些条件满足后触发线程的执行等功能。 3. 原子操作类:并发包中提供了一系列原子操作类,如AtomicInteger、AtomicLong等,用于在多线程环境下对变量进行原子操作。这些类通过使用CAS(Compare and Swap)操作来保证变量的原子性,避免了使用synchronized关键字进行同步操作带来的性能开销。 4. 并发线程安全工具类:并发包中还提供了一些线程安全的辅助类,如CopyOnWriteArrayList、ConcurrentHashMap等,用于替代传统的非线程安全集合类。这些类通过使用一些特定的并发算法来保证多线程环境下的线程安全性,能够在并发读写的情况下提供较好的性能。 总之,java的并发包不仅提供了高性能的线程安全集合类,还提供了一些并发执行框架、并发工具类、原子操作类以及线程安全工具类,用于帮助开发者在多线程环境下更加方便、高效地编写并发代码。这些功能的引入使得开发者能够更好地处理并发程序的编写与调试,提高了程序的性能和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值