线程锁Lock
使用相较于synchronized更加灵活,在jdk5时性能比synchronized要好
在jdk7后,synchronized也升级了,单用性能上差不多(快慢),功能上lock更多
LOCK是接口
void lock():获取锁,如锁被占用则等待 相当于排队
boolean trylock():获取锁,获取成功返回true 相当于排号
void unlock():释放锁
实现类:
一、重入锁:ReentrantLock,与synchronized一样具有互斥锁功能
Lock lock=new ReentrantLock() 可带参数,也可不带,(带参数即忽略线程优先级)
1.释放锁需要确保即使发生异常也要释放,即需要放在finally里
2.何为重入锁:当在一定的情形下,比如递归调用自身,也可以拿到锁,但是拿到锁的数量与释放锁的数量需要相同。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ik12huhP-1613900073951)(C:\Users\Think\AppData\Roaming\Typora\typora-user-images\image-20210117152350301.png)]
二、读写锁:ReentrantReadWriteLock(支持递归调用)
1.一种支持一写多读的同步锁,读写分离,可分别分配读和写锁
2.支持多次分配读锁,使多个操作可以并发执行。
ReadLock() 读 WriteLock() 写
ReentrantReadWriteLock rwl=new ReentrantReadWriteLock()
ReadLock read=rwl.readLock()
WriteLock write=rwl.writeLock()
ReadLock 和WriteLock 是ReentrantReadWriteLock 的内部实现类
读与写是分开的,有两把
读:读操作不会改变数据,所以不需要互斥操作
写:会改变数据,需要互斥操作
当读比写多时,该锁效率比较高。
互斥规则:
写于写:互斥,阻塞
读与写:互斥,读阻塞写,写阻塞读
读与读:不互斥,不阻塞:读操作高于写操作
线程安全的集合
除了vector(Collection)和hashtable(map),其他集合都是线程不安全的
Vector
Vector和ArrayList类似,是长度可变的数组,与ArrayList不同的是,Vector是线程安全的,它给几乎所有的public方法都加上了synchronized关键字。由于加锁导致性能降低,在不需要并发访问同一对象时,这种强制性的同步机制就显得多余,所以现在Vector已被弃用。
HashTable
HashTable和HashMap类似,不同点是HashTable是线程安全的,它给几乎所有public方法都加上了synchronized关键字,还有一个不同点是HashTable的K,V都不能是null,但HashMap可以,它现在也因为性能原因被弃用了。
Collection接口下的:
虽然这些都不是线程安全的集合,但是可以使用工具类Collections来实现线程安全。
Collections提供了多个可以获得线程安全集合的方法。
List synArrayList = Collections.synchronizedList(new ArrayList());
Set synHashSet = Collections.synchronizedSet(new HashSet());
Map<K,V> synHashMap = Collections.synchronizedMap(new HashMap<K,V>());
底层实现:
举例:ArrayList list=new ArrayList();
List safeList=Collections.synchronizedList(list);
safelist.add(“A”);
当使用Collections.synchronizedList(list)变为安全时,由原先的ArrayList对象变为了安全的Collection&synchronizedCollection对象,此时它调用的add方法还是原先的ArrayList方法,但是在此方法之前加了一个锁(属性)对象(Final object matex),当多个线程访问这个集合时,相当于都需要拿到这个锁对象才能访问,那么此时就可以实现线程安全。
接口统一,维护性高,但性能没有提高,均已synchronized实现。(了解)
思想:代理(在外围进行包装)------>代理模式
CopeOnWriteArrayList
相较于Collections工具类来实现安全集合,它单独做了包装。
当遍历操作(读)多于写可变操作(写)是,该集合性能较好。
线程安全的ArrayList,加强版的读写分离,优于读写锁:
why?
在其底层使用了一个互斥锁(ReentrantLock),只在可变操作上加锁,而在读操作上不加锁,此时就只有写与写有互斥,因为其底层使用的是cope操作,即开辟新空间进行添加数据,然后重新更新原数组的地址,那么此时就不会对正在读以及正在写造成影响。
CopeOnWriteArraySet
线程安全的set,底层是CopeOnWriteArrayList实现
唯一不同,使用addIfAbsent(),添加元素,遍历数组。
区别:如何解决去重问题
调用CopeOnWriteArrayList类里面的方法,多了利用for循环判断是否有重复数据,其他的操作是一样的。
ConcurrentHashMap
- 初始容量认为16段(segment),使用分段锁设计。
- 不对整个map加锁,而是为每个段加锁
- 当多个对象存入同一个段时,才需要互斥
- 最理想状态为16个对象分别存入16个段中,并行数量16
- 使用方式与HashMap无异
Map<String, String> map = new ConcurrentHashMap<>();
Queue(接口)
队列,线程不安全
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nhDamcmY-1613900073963)(C:\Users\Think\AppData\Roaming\Typora\typora-user-images\image-20210118093752927.png)]
ConcurrentLinkedQueue
线程安全的Queue,可高效读写的队列,高并发下性能最好的队列
无锁,CAS比较交换算法,修改的方法包含三个参数(V,E,N)
V:要跟新的变量 E:预期值 N新值
只有当V==E时,V=N,否则表示已被更新,则取消当前操作
BlockingQueue(接口)
线程安全下进行通信
Queue的子接口,阻塞的队列,增加了两个线程状态为无限期等待的方法
可用于解决生产者、消费者问题。(线程通信问题)
实现类:
ArrayBlockingQueue
LinkedBlockingQueue