学习Java第三十天--多线程之线程安全的集合

14.5 线程安全的集合

14.5.1 线程安全集合图

  • Collection体系集合下,除Vector以外的线程安全集合;
    在这里插入图片描述

14.5.2 Collections中的工具方法

Collections工具类中提供了多个可以获得线程安全集合的方法;

  • public static Collection synchronizedCollection(Collection
    c)
  • public static List synchronizedList(List list)
  • public static Set synchronizedSet(Set s)
  • public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
  • public static SortedSet synchronizedSortedSet(SortedSet s)
  • public static <K,V> SortedMap<K,V>
  • synchronizedSortedMap(SortedMap<K,V> m)

JDK1.2提供,接口统一、维护性高,但性能没有提升,均以synchronized实现;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class TestCollectionsForSyn {

	public static void main(String[] args) {
		
		List<String> list = new ArrayList<String>();
		
		//传入一个线程不安全的List,返回一个线程安全的List
		List<String> safeList = Collections.synchronizedList(list);
	
		//和普通集合应用无差别
		safeList.add("A");//SynchronizedList里的add方法,该方法里加了个锁
		safeList.add("B");
		safeList.add("C");
		safeList.remove(1);
		safeList.get(1);
		
		
		Set<String> set = new HashSet<String>();
		Set<String> newSet = Collections.synchronizedSet(set);
		newSet.add("A");
		newSet.add("B");
		newSet.add("C");
		for(String s : newSet) {
			System.out.println(s);
		}
	}

}

14.5.3 CopyOnWriteArrayList

  • 线程安全的ArrayList,加强版读写分离;
  • 写有锁,读无锁,读写之间不阻塞,优于读写锁;
  • 写入时,先copy一个容器副本、再添加新元素,最后替换引用;
  • 使用方法与ArrayList无异;

14.5.4 CopyOnWriteArraySet

  • 线程安全的Set,底层使用CopyOnWriteArrayList实现;
  • 唯一不同在于,使用addIfAbsent()添加元素,会遍历数组;
  • 如存在元素,则不添加(扔掉副本);

14.5.5 ConcurrentHashMap

  • 初始容量默认为16段(Segment),使用分段锁设计;
  • 不对整个Map加锁,而是为每个Segment加锁;
  • 当多个对象存入同一个Segment时,才需要互斥;
  • 最理想状态为16个对象分别存入16个Segment,并行数量16;
  • 使用方式与HashMap无异;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

public class TestCopyOnWriteArrayList {

	public static void main(String[] args) {
		//写有锁,读无锁的集合
		CopyOnWriteArrayList<String> alist = new CopyOnWriteArrayList<String>();
		
		//写操作
		alist.add("A");//都将底层数组做了一次复制,写的是新数组,完成赋值后,再将新数组替换掉旧数组
		alist.add("B");//没调用一次,底层方法扩容一次
		
		//读操作,无锁
		alist.get(1);//读的是写操作完成之前的旧数组,写完之后,才能读到新数组的新值

		//无序,无下标,不允许重复
		CopyOnWriteArraySet<String> aset = new CopyOnWriteArraySet<String>();
		
		//写操作,表面使用的是add方法;底层实际是用的CopyOnWriteArrayList的addIfAbsent()来判断要插入的新值是否存在
		aset.add("A");
		aset.add("B");
		aset.add("C");
		
		for(String s : aset) {
			System.out.println(s);
		}
		
		HashMap<String , String> maps = new HashMap<String , String>();
		
		//分段锁设计  Segment  JDK1.7的做法
		ConcurrentHashMap<String , String> ch = new ConcurrentHashMap<String , String>();
		//1.8的做法       CAS交换算法和同步锁 同步锁锁的是表头对象(链表的第一个),拿到锁的对象要先做节点遍历,查看有没有相同的key,相同覆盖,不同,则挂在最后一个节点的next上
		
		
		ch.put("A", "1");
		ch.keySet();
	}

}

14.5.6 Queue接口(队列)

  • Collection的子接口,表示队列FIFO(First In First Out);

常用方法:

  • 抛出异常:
    boolean add(E e) //顺序添加一个元素(到达上限后,再添加则会抛出异常)
    E remove() //获得第一个元素并移除(如果队列没有元素时,则抛出异常)
    E element() //获得第一个元素但不移除(如果队列没有元素时,则抛出异常)
  • 返回特殊值:推荐使用
    boolean offer(E e) //顺序添加一个元素(到达上限后,再添加则会返回false)
    E poll() //获得第一个元素并移除(如果队列没有元素时,则返回null)
    E keep() //获得第一个元素但不移除(如果队列没有元素时,则返回null)

14.5.7 ConcurrentLinkedQueue

  • 线程安全、可高效读写的队列,高并发下性能最好的队列;
  • 无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N);
  • V : 要更新的变量、E:预期值、N:新值;
  • 只有当V == E 时,V = N ; 否则表示已被更新过,则取消当前操作;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

public class TestQueue {

	public static void main(String[] args) {
		
		//Queue
		//列表,尾部添加(指定下标添加)
		//链表,头尾添加
		//队列,FIFO
		//Queue<String> qu = new LinkedList<String>();//遵循队列规则的链表
		LinkedList<String> link = new LinkedList<String>();
		
		link.offer("A");
		link.offer("B");
		link.offer("C");
		
		//用列表的方式打乱了FIFO队列
		link.add(0,"D");
		//强制LinkedList后,不能调用带有下标的add方法
		
		System.out.println(link.peek());//队列中的第一个元素
		
		//严格遵守理论队列的规则,且是线程安全的,采用了CAS交换算法
		Queue<String> q = new ConcurrentLinkedQueue<String>();
		//1.抛出异常的  2.返回结果的
		q.offer("A");
		q.offer("B");
		q.offer("C");
		
		q.poll();//删除表头
		System.out.println(q.peek());//获得表头
	}

}

14.5.8 BlockingQueue接口(阻塞队列)

  • Queue的子接口,阻塞的队列,增加了两个线程状态为无期限等待的方法;

方法:

  • void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待;
  • E take() // 获取并移除此队列头部元素,如果没有可用元素,则等待;
  • 可用于解决生产者、消费者问题;

14.5.9 阻塞队列

  • ArrayBlockingQueue:
    数组结构实现,有界队列(手工固定上限)

  • LinkedBlockingQueue:
    链表结构实现,无界队列(默认上限Integer.MAX_VALUE)

14.6 总结

  • ExecutorSevice线程池接口、Executors工厂;
  • Callable线程任务、Future异步返回值;
  • Lock、ReentrantLock重入锁、ReentrantReadWriteLock读写锁;
  • CopyOnWriteArrayList线程安全的ArrayList;
  • CopyOnWriteArraySet线程安全的Set;
  • ConcurrentHashMap线程安全的HashMap;
  • ConcurrentLinkedQueue线程安全的Queue;
  • ArrayBlockingQueue线程安全的阻塞Queue;(生产者、消费者)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值