juc包下的并发容器 线程池及任务超时管理

JUC 并发工具包:java.util.concurrent包:定义了并发编程中很常用的实用工具类。
在这里插入图片描述

1.ConcurrentHashMap
对应的非并发容器:HashMap
目标:代替Hashtable、synchronizedMap,支持复合操作
原理:JDK6中采用一种更加细粒度的加锁机制Segment“分段锁”,JDK8中采用CAS无锁算法。

2.CopyOnWriteArrayList
对应的非并发容器:ArrayList
目标:代替Vector、synchronizedList
原理:利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面修改,然后将新集合赋值给旧的引用,并通过volatile 保证其可见性,当然写操作的锁是必不可少的了。
内部实现为ReentrantLock作为锁实现操作互斥
Object[] 来存储数据 volatile修饰 保证可见性
对数组进行元素变化时,通过复制一个新的数组实现 开销比较大
读取操作没有任何同步控制和锁操作,理由就是内部数组 array 不会发生修改,只会被另外一个 array 替换,因此可以保证数据安全。
CopyOnWriteArrayList 写入操作 add() 方法在添加集合的时候加了锁,保证同步,避免多线程写的时候会 copy 出多个副本。

3.CopyOnWriteArraySet
对应的非并发容器:HashSet
目标:代替synchronizedSet
原理:基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法,其遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。

4.ConcurrentSkipListMap
对应的非并发容器:TreeMap
目标:代替synchronizedSortedMap(TreeMap)
原理:Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。

5.ConcurrentSkipListSet
对应的非并发容器:TreeSet
目标:代替synchronizedSortedSet
原理:内部基于ConcurrentSkipListMap实现

6.ConcurrentLinkedQueue
不会阻塞的队列
对应的非并发容器:Queue
原理:基于链表实现的FIFO队列(LinkedList的并发版本)

7.LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue
对应的非并发容器:BlockingQueue
特点:拓展了Queue,增加了可阻塞的插入和获取等操作
原理:通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒
实现类:
LinkedBlockingQueue:基于链表实现的可阻塞的FIFO队列
ArrayBlockingQueue:基于数组实现的可阻塞的FIFO队列
PriorityBlockingQueue:按优先级排序的队列
在这里插入图片描述

ConcurrentHashMap的实现:

HashMap,Hashtable与ConcurrentHashMap都是实现的哈希表数据结构,在随机读取的时候效率很高。
Hashtable实现同步是利用synchronized关键字进行锁定的,其是针对整张哈希表进行锁定的,即每次锁住整张表让线程独占,在线程安全的背后是巨大的浪费。
ConcurrentHashMap和Hashtable主要区别就是围绕着锁的粒度进行区别以及如何区锁定。
上图中,左边是Hashtable的实现方式,可以看到锁住整个哈希表;而右边则是ConcurrentHashMap的实现方式,单独锁住每一个桶(segment).ConcurrentHashMap将哈希表分为16个桶(默认值),诸如get(),put(),remove()等常用操作只锁当前需要用到的桶,而size()才锁定整张表。
原来只能一个线程进入,现在却能同时接受16个写线程并发进入(写线程需要锁定,而读线程几乎不受限制)。所以,才有了并发性的极大提升。
高并发编程,除了并发容器,还会涉及到并发工具类:CountDownLatch等,后续将详细的介绍并发工具类,以及ConcurrentHashMap的底层实现细节,不仅要知其然,还要知其所以然,这样才能更好的掌握好高并发编程。

J.U.C进行体系图
在这里插入图片描述
在这里插入图片描述

=以下内容作为了解==
1、什么是同步容器
Java的集合容器框架中,主要有四大类别:List、Set、Queue、Map,大家熟知的这些集合类ArrayList、LinkedList、HashMap这些容器都是非线程安全的。
如果有多个线程并发地访问这些容器时,就会出现问题。
因此,在编写程序时,在多线程环境下必须要求程序员手动地在任何访问到这些容器的地方进行同步处理,这样导致在使用这些容器的时候非常地不方便。
所以,Java先提供了同步容器供用户使用。
同步容器可以简单地理解为通过synchronized来实现同步的容器,比如Vector、Hashtable以及SynchronizedList等容器。

2、同步容器,主要的分类:
Vector
Stack
HashTable
Collections.synchronized方法生成

3、同步容器面临的问题
可以通过查看Vector,Hashtable等这些同步容器的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上关键字synchronized。
这样做的代价是削弱了并发性,当多个线程共同竞争容器级的锁时,吞吐量就会降低。
例如: HashTable只要有一条线程获取了容器的锁之后,其他所有的线程访问同步函数都会被阻塞,因此同一时刻只能有一条线程访问同步函数。
因此为了解决同步容器的性能问题,所以才有了并发容器。

4、什么是并发容器
java.util.concurrent包中提供了多种并发类容器。
并发类容器是专门针对多线程并发设计的,使用了锁分段技术,只对操作的位置进行同步操作,但是其他没有操作的位置其他线程仍然可以访问,提高了程序的吞吐量。
采用了CAS算法和部分代码使用synchronized锁保证线程安全。

==以下内容作为了解=
一些基础比较
volatile 变量:轻量级多线程同步机制,不会引起上下文切换和线程调度。仅提供内存可见性保证,不提供原子性。

CAS 原子指令:轻量级多线程同步机制,不会引起上下文切换和线程调度。它同时提供内存可见性和原子化更新保证。

内部锁和显式锁:重量级多线程同步机制,可能会引起上下文切换和线程调度,它同时提供内存可见性和原子性。

概念总览:
在这里插入图片描述

volatile变量: 轻量级的锁
线程可见性:保证每一个线程可以实时知道volatile变量的值变化。对于多线程,不是一种互斥关系,不能保证变量状态的“原子性操作”

原子变量的基础:CAS算法
一种无锁的非阻塞算法的实现,首先,CPU 会将内存中将要被更改的数据与期望的值做比较。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。最后,CPU 会将旧的数值返回。这一系列的操作是原子的。

CAS 包含了3 个操作数:

需要读写的内存值V
进行比较的值A
拟写入的新值B
java.util.concurrent.atomic包下提供了一些原子操作的常用类:
AtomicBoolean
AtomicInteger
AtomicLong
AtomicReference
AtomicIntegerArray
AtomicLongArray
AtomicMarkableReference
AtomicReferenceArray
AtomicStampedReference
核心方法: boolean compareAndSet(expectedValue, updateValue)

AQS(AbstractQueuedSynchronizer)
抽象队列同步器AQS:维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。

state: 来维护同步状态,
FIFO队列: 来完成资源获取线程的排队工作。
AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

基于AQS的锁:
Reentrant
Semaphore
CountDownLatch
ReentrantReadWriteLock
SynchronousQueue
FutureTask
AQS通过getState、setState及compareAndSetState等protected类型方法进行状态转换。

ReentrantLock用state表示所有者线程已经重复获取该锁的次数。
Semaphore用state表示剩余的许可数量。
CountDownLatch用state表示闭锁的状态,如关闭、打开。
FutureTask用state表示任务的状态,如尚未开始、正在运行、已完成、已取消。
除了state,在同步器类中还可以自行管理一些额外的状态变量。如:

ReentrantLock保存了锁的当前所有者的信息,这样就能区分某个获取操作是重入的还是竞争的。
FutureTask用result表示任务的结果,该结果可能是计算得到的答案,也可能是抛出的异常。
并发容器
JUC 提供了多种并发容器类来改进同步容器的性能。如:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、 CopyOnWriteArrayList 和 CopyOnWriteArraySet。

ConcurrentHashMap
ConcurrentHashMap 在JDK 7之前是通过Lock和Segment(分段锁)实现并发安全,JDK 8之后改为CAS+synchronized来保证并发安全。

ConcurrentHashMap、HashMap和HashTable的区别:
Hashtable 是线程安全的哈希表,由于是通过内置锁 synchronized 来保证线程安全,在资源争用比较高的环境下,Hashtable 的效率比较低,不建议使用。

HashMap 是非线程安全的哈希表,常用于单线程程序中。

ConcurrentHashMap 是一个支持并发操作的线程安全的HashMap,但是他不允许存储空key或value。使用CAS+synchronized来保证并发安全,在并发访问时不需要阻塞线程,所以效率是比Hashtable 要高的。

ConcurrentHashMap并不是将每个方法在同一个锁上同步并使得每次只能有一个线程访问容器,而是使用一种粒度更细的加锁机制来实现更大程度的共享,这种机制称为“分段锁”。

segments 为整个ConcurrentHashMap,segment为分段,table时分段的具体内容,HashBucket是桶形式的hash存储内容,HashEntry是具体的数据节点。

ConcurrentHashMap的出现主要是提高了hashmap在多线程下的安全性,可以理解是hashmap在多线程上的替代。

CountDownLatch 闭锁
CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行

CountDownLatch是通过一个计数器来实现的,当我们在new 一个CountDownLatch对象的时候需要带入该计数器值,该值就表示了线程的数量。每当一个线程完成自己的任务后,计数器的值就会减1。当计数器的值变为0时,就表示所有的线程均已经完成了任务,然后就可以恢复等待的线程继续执行了。

使用场景:
需要等待某个条件达到要求后才能做后面的事情;
同时当线程都完成后也会触发事件,以便进行后面的操作。
CountDownLatch最重要的方法是
countDown(): 计数减一
await():等待计数为0,如果没有到达0,就只有阻塞等待了。
Callable 接口
执行Callable,需要FutureTask实现类的支持,用于接受运算的结果。FutureTask是Future接口的实现类。

示例:

//注入实现Callable接口的类,必须重写其中的call方法。
FutureTask result = new FutureTask<>(threadDemo);
new Thread(result).start();

class ThreadDemo implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
}
}
Callable 接口类似于 Runnable,两者都可以实现多线程,其中Runnable无返回值,而Callable有返回值,但是Callable实现需要依赖Future接口实现的类。其中Callable重写call方法,Runnable重写run方法。

ReentrantLock 类
ReentrantLock 实现了 Lock 接口,ReentrantLock实现Lock有两种模式即公平模式和不公平模式
,参看java文档介绍如下。

一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。

此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。与采用默认设置(使用不公平锁)相比,使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁和保证锁分配的均衡性时差异较小。不过要注意的是,公平锁不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。还要注意的是,未定时的 tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁是可用的,此方法就可以获得成功。

class X {
private final ReentrantLock lock = new ReentrantLock();
// …

public void m() { 
lock.lock();  // block until condition holds
try {
    // ... method body
} finally {
    // 一定记得要在finally中释放锁。
    lock.unlock()
}
}

}
ReentrantLock里面的功能函数主要有:
lock() //阻塞模式获取资源
‍lockInterruptibly() //如果当前线程未被 中断,则获取锁。
tryLock() //仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
tryLock(time) //在一段时间内尝试获取资源
‍unlock() //释放资源,为了保证一定执行,放在finally中执行。
ReadWriteLock && ReentrantReadWriteLock
读-写锁ReadWriteLock:维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有writer,读取锁可以由多个reader 线程同时保持。写入锁是独占的。

ReadWriteLock 读取操作通常不会改变共享资源,但执行写入操作时,必须独占方式来获取锁。对于读取操作占多数的数据结构。ReadWriteLock 能提供比独占锁更高的并发性。而对于只读的数据结构,其中包含的不变性可以完全不需要考虑加锁操作。

Lock readLock() //返回用于读取操作的锁。
Lock writeLock() //返回用于写入操作的锁。

1.ConcurrentHashMap
对应的非并发容器:HashMap
目标:代替Hashtable、synchronizedMap,支持复合操作
原理:JDK6中采用一种更加细粒度的加锁机制Segment“分段锁”,JDK8中采用CAS无锁算法。

2.CopyOnWriteArrayList
对应的非并发容器:ArrayList
目标:代替Vector、synchronizedList
原理:利用高并发往往是读多写少的特性,对读操作不加锁,对写操作,先复制一份新的集合,在新的集合上面修改,然后将新集合赋值给旧的引用,并通过volatile 保证其可见性,当然写操作的锁是必不可少的了。
内部实现为ReentrantLock作为锁实现操作互斥
Object[] 来存储数据 volatile修饰 保证可见性
对数组进行元素变化时,通过复制一个新的数组实现 开销比较大
读取操作没有任何同步控制和锁操作,理由就是内部数组 array 不会发生修改,只会被另外一个 array 替换,因此可以保证数据安全。
CopyOnWriteArrayList 写入操作 add() 方法在添加集合的时候加了锁,保证同步,避免多线程写的时候会 copy 出多个副本。

3.CopyOnWriteArraySet
对应的非并发容器:HashSet
目标:代替synchronizedSet
原理:基于CopyOnWriteArrayList实现,其唯一的不同是在add时调用的是CopyOnWriteArrayList的addIfAbsent方法,其遍历当前Object数组,如Object数组中已有了当前元素,则直接返回,如果没有则放入Object数组的尾部,并返回。

4.ConcurrentSkipListMap
对应的非并发容器:TreeMap
目标:代替synchronizedSortedMap(TreeMap)
原理:Skip list(跳表)是一种可以代替平衡树的数据结构,默认是按照Key值升序的。

5.ConcurrentSkipListSet
对应的非并发容器:TreeSet
目标:代替synchronizedSortedSet
原理:内部基于ConcurrentSkipListMap实现

6.ConcurrentLinkedQueue
不会阻塞的队列
对应的非并发容器:Queue
原理:基于链表实现的FIFO队列(LinkedList的并发版本)

7.LinkedBlockingQueue、ArrayBlockingQueue、PriorityBlockingQueue
对应的非并发容器:BlockingQueue
特点:拓展了Queue,增加了可阻塞的插入和获取等操作
原理:通过ReentrantLock实现线程安全,通过Condition实现阻塞和唤醒
实现类:
LinkedBlockingQueue:基于链表实现的可阻塞的FIFO队列
ArrayBlockingQueue:基于数组实现的可阻塞的FIFO队列
PriorityBlockingQueue:按优先级排序的队列

ConcurrentHashMap的实现:

HashMap,Hashtable与ConcurrentHashMap都是实现的哈希表数据结构,在随机读取的时候效率很高。
Hashtable实现同步是利用synchronized关键字进行锁定的,其是针对整张哈希表进行锁定的,即每次锁住整张表让线程独占,在线程安全的背后是巨大的浪费。
ConcurrentHashMap和Hashtable主要区别就是围绕着锁的粒度进行区别以及如何区锁定。
上图中,左边是Hashtable的实现方式,可以看到锁住整个哈希表;而右边则是ConcurrentHashMap的实现方式,单独锁住每一个桶(segment).ConcurrentHashMap将哈希表分为16个桶(默认值),诸如get(),put(),remove()等常用操作只锁当前需要用到的桶,而size()才锁定整张表。
原来只能一个线程进入,现在却能同时接受16个写线程并发进入(写线程需要锁定,而读线程几乎不受限制)。所以,才有了并发性的极大提升。
高并发编程,除了并发容器,还会涉及到并发工具类:CountDownLatch等,后续将详细的介绍并发工具类,以及ConcurrentHashMap的底层实现细节,不仅要知其然,还要知其所以然,这样才能更好的掌握好高并发编程。

J.U.C进行体系图

=以下内容作为了解==
1、什么是同步容器
Java的集合容器框架中,主要有四大类别:List、Set、Queue、Map,大家熟知的这些集合类ArrayList、LinkedList、HashMap这些容器都是非线程安全的。
如果有多个线程并发地访问这些容器时,就会出现问题。
因此,在编写程序时,在多线程环境下必须要求程序员手动地在任何访问到这些容器的地方进行同步处理,这样导致在使用这些容器的时候非常地不方便。
所以,Java先提供了同步容器供用户使用。
同步容器可以简单地理解为通过synchronized来实现同步的容器,比如Vector、Hashtable以及SynchronizedList等容器。

2、同步容器,主要的分类:
Vector
Stack
HashTable
Collections.synchronized方法生成

3、同步容器面临的问题
可以通过查看Vector,Hashtable等这些同步容器的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上关键字synchronized。
这样做的代价是削弱了并发性,当多个线程共同竞争容器级的锁时,吞吐量就会降低。
例如: HashTable只要有一条线程获取了容器的锁之后,其他所有的线程访问同步函数都会被阻塞,因此同一时刻只能有一条线程访问同步函数。
因此为了解决同步容器的性能问题,所以才有了并发容器。

4、什么是并发容器
java.util.concurrent包中提供了多种并发类容器。
并发类容器是专门针对多线程并发设计的,使用了锁分段技术,只对操作的位置进行同步操作,但是其他没有操作的位置其他线程仍然可以访问,提高了程序的吞吐量。
采用了CAS算法和部分代码使用synchronized锁保证线程安全。

==以下内容作为了解=
一些基础比较
volatile 变量:轻量级多线程同步机制,不会引起上下文切换和线程调度。仅提供内存可见性保证,不提供原子性。

CAS 原子指令:轻量级多线程同步机制,不会引起上下文切换和线程调度。它同时提供内存可见性和原子化更新保证。

内部锁和显式锁:重量级多线程同步机制,可能会引起上下文切换和线程调度,它同时提供内存可见性和原子性。

概念总览:

volatile变量: 轻量级的锁
线程可见性:保证每一个线程可以实时知道volatile变量的值变化。对于多线程,不是一种互斥关系,不能保证变量状态的“原子性操作”

原子变量的基础:CAS算法
一种无锁的非阻塞算法的实现,首先,CPU 会将内存中将要被更改的数据与期望的值做比较。然后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。否则便不做操作。最后,CPU 会将旧的数值返回。这一系列的操作是原子的。

CAS 包含了3 个操作数:

需要读写的内存值V
进行比较的值A
拟写入的新值B
java.util.concurrent.atomic包下提供了一些原子操作的常用类:
AtomicBoolean
AtomicInteger
AtomicLong
AtomicReference
AtomicIntegerArray
AtomicLongArray
AtomicMarkableReference
AtomicReferenceArray
AtomicStampedReference
核心方法: boolean compareAndSet(expectedValue, updateValue)

AQS(AbstractQueuedSynchronizer)
抽象队列同步器AQS:维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。

state: 来维护同步状态,
FIFO队列: 来完成资源获取线程的排队工作。
AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore/CountDownLatch)。

基于AQS的锁:
Reentrant
Semaphore
CountDownLatch
ReentrantReadWriteLock
SynchronousQueue
FutureTask
AQS通过getState、setState及compareAndSetState等protected类型方法进行状态转换。

ReentrantLock用state表示所有者线程已经重复获取该锁的次数。
Semaphore用state表示剩余的许可数量。
CountDownLatch用state表示闭锁的状态,如关闭、打开。
FutureTask用state表示任务的状态,如尚未开始、正在运行、已完成、已取消。
除了state,在同步器类中还可以自行管理一些额外的状态变量。如:

ReentrantLock保存了锁的当前所有者的信息,这样就能区分某个获取操作是重入的还是竞争的。
FutureTask用result表示任务的结果,该结果可能是计算得到的答案,也可能是抛出的异常。
并发容器
JUC 提供了多种并发容器类来改进同步容器的性能。如:ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、 CopyOnWriteArrayList 和 CopyOnWriteArraySet。

ConcurrentHashMap
ConcurrentHashMap 在JDK 7之前是通过Lock和Segment(分段锁)实现并发安全,JDK 8之后改为CAS+synchronized来保证并发安全。

ConcurrentHashMap、HashMap和HashTable的区别:
Hashtable 是线程安全的哈希表,由于是通过内置锁 synchronized 来保证线程安全,在资源争用比较高的环境下,Hashtable 的效率比较低,不建议使用。

HashMap 是非线程安全的哈希表,常用于单线程程序中。

ConcurrentHashMap 是一个支持并发操作的线程安全的HashMap,但是他不允许存储空key或value。使用CAS+synchronized来保证并发安全,在并发访问时不需要阻塞线程,所以效率是比Hashtable 要高的。

ConcurrentHashMap并不是将每个方法在同一个锁上同步并使得每次只能有一个线程访问容器,而是使用一种粒度更细的加锁机制来实现更大程度的共享,这种机制称为“分段锁”。

segments 为整个ConcurrentHashMap,segment为分段,table时分段的具体内容,HashBucket是桶形式的hash存储内容,HashEntry是具体的数据节点。

ConcurrentHashMap的出现主要是提高了hashmap在多线程下的安全性,可以理解是hashmap在多线程上的替代。

CountDownLatch 闭锁
CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行

CountDownLatch是通过一个计数器来实现的,当我们在new 一个CountDownLatch对象的时候需要带入该计数器值,该值就表示了线程的数量。每当一个线程完成自己的任务后,计数器的值就会减1。当计数器的值变为0时,就表示所有的线程均已经完成了任务,然后就可以恢复等待的线程继续执行了。

使用场景:
需要等待某个条件达到要求后才能做后面的事情;
同时当线程都完成后也会触发事件,以便进行后面的操作。
CountDownLatch最重要的方法是
countDown(): 计数减一
await():等待计数为0,如果没有到达0,就只有阻塞等待了。
Callable 接口
执行Callable,需要FutureTask实现类的支持,用于接受运算的结果。FutureTask是Future接口的实现类。

示例:

//注入实现Callable接口的类,必须重写其中的call方法。
FutureTask result = new FutureTask<>(threadDemo);
new Thread(result).start();

class ThreadDemo implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
}
}
Callable 接口类似于 Runnable,两者都可以实现多线程,其中Runnable无返回值,而Callable有返回值,但是Callable实现需要依赖Future接口实现的类。其中Callable重写call方法,Runnable重写run方法。

ReentrantLock 类
ReentrantLock 实现了 Lock 接口,ReentrantLock实现Lock有两种模式即公平模式和不公平模式
,参看java文档介绍如下。

一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。

ReentrantLock 将由最近成功获得锁,并且还没有释放该锁的线程所拥有。当锁没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁并返回。如果当前线程已经拥有该锁,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。

此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。与采用默认设置(使用不公平锁)相比,使用公平锁的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁和保证锁分配的均衡性时差异较小。不过要注意的是,公平锁不能保证线程调度的公平性。因此,使用公平锁的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁时。还要注意的是,未定时的 tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁是可用的,此方法就可以获得成功。

class X {
private final ReentrantLock lock = new ReentrantLock();
// …

public void m() { 
lock.lock();  // block until condition holds
try {
    // ... method body
} finally {
    // 一定记得要在finally中释放锁。
    lock.unlock()
}
}

}
ReentrantLock里面的功能函数主要有:
lock() //阻塞模式获取资源
‍lockInterruptibly() //如果当前线程未被 中断,则获取锁。
tryLock() //仅在调用时锁未被另一个线程保持的情况下,才获取该锁。
tryLock(time) //在一段时间内尝试获取资源
‍unlock() //释放资源,为了保证一定执行,放在finally中执行。
ReadWriteLock && ReentrantReadWriteLock
读-写锁ReadWriteLock:维护了一对相关的锁,一个用于只读操作,另一个用于写入操作。只要没有writer,读取锁可以由多个reader 线程同时保持。写入锁是独占的。

ReadWriteLock 读取操作通常不会改变共享资源,但执行写入操作时,必须独占方式来获取锁。对于读取操作占多数的数据结构。ReadWriteLock 能提供比独占锁更高的并发性。而对于只读的数据结构,其中包含的不变性可以完全不需要考虑加锁操作。

Lock readLock() //返回用于读取操作的锁。
Lock writeLock() //返回用于写入操作的锁。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

星空 | 永恒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值