一、概述
1. Concurrent包是JDK1.5提供的一个用于应对高并发的包
2. 包含5块主要内容:BlockingQueue、 ConcurrentMap、 ExecutorService、 Lock、 Atomic操作
BlockingQueue - 阻塞式队列
一、概述
1. 满足队列的特性:FIFO
2. 阻塞式队列是有界的,既大小固定不变
3. 阻塞:
a. 如果队列已满,则新添元素的线程会被阻塞
b. 如果队列为空,则获取元素的线程会被阻塞
4.方法:
5. BlockingQueue中不允许存储null
6. 适用场景:生产消费
二、实现类
-
ArrayBlockingQueue - 阻塞式顺序队列
a. 底层是基于数组来存储数据
b. 使用的时候需要指定容量 -
LinkedBlockingQueue - 阻塞式链式队列
a. 操作和ArrayBlockingQueue是一样的
b. 底层是基于节点来存储数据
c. 在使用的时候可以不指定容量,如果不指定则容量默认为Integer.MAX_VALUE,也因为这个容量比较大,导致在使用过程中很少会将这个队列放满,所以一般认为这个队列是无界的 -
PriorityBlockingQueue - 具有优先级的阻塞式队列
a. 可以不指定容量,如果不指定,则默认容量是11
b. 在遍历队列的时候,会对元素进行自然排序,要求元素对应的类要实现Comparable的接口
c. 如果使用迭代遍历,则不保证排序 -
SynchronousQueue - 同步队列
a. 使用的时候不需要指定容量,默认容量为1并且只能为1
b. 一般称这个队列是数据的汇合点
package cn.tedu.blockingqueue;
//import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
// 大小指定之后就不可变
// ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<>();
// 队列为空
// 抛出异常
// System.out.println(queue.remove());
// 返回null
// System.out.println(queue.poll());
// 产生阻塞
// System.out.println(queue.take());
// 定时阻塞
System.out.println(queue.poll(5, TimeUnit.SECONDS));
// 添加元素
// queue.add("a");
// queue.add("a");
// queue.add("a");
// 队列已满
// 抛出异常 - IllegalStateException
// queue.add("b");
// 返回false
// boolean b = queue.offer("c");
// System.out.println(b);
// 产生阻塞
// queue.put("d");
// 定时阻塞
// queue.offer("e", 5, TimeUnit.SECONDS);
// System.out.println(queue);
}
}
package cn.tedu.blockingqueue;
import java.util.concurrent.PriorityBlockingQueue;
public class PriorityBlockingQueueDemo {
public static void main(String[] args) throws InterruptedException {
// PriorityBlockingQueue<String> queue = new PriorityBlockingQueue<>();
//
// // 添加元素
// queue.put("d");
// queue.put("e");
// queue.put("a");
// queue.put("h");
// queue.put("u");
// queue.put("r");
// queue.put("s");
PriorityBlockingQueue<Student> queue = new PriorityBlockingQueue<>();
queue.add(new Student("张三", 18, 59));
queue.add(new Student("李四", 17, 69));
queue.add(new Student("王五", 20, 48));
queue.add(new Student("赵六", 15, 62));
queue.add(new Student("陆七", 25, 40));
queue.add(new Student("杨八", 10, 18));
queue.add(new Student("陈九", 19, 61));
// for (int i = 0; i < 7; i++) {
// System.out.println(queue.take());
// }
// 增强for循环本质上是一种迭代遍历
for (Student student : queue) {
System.out.println(student);
}
}
}
class Student implements Comparable<Student> {
private String name;
private int age;
private int score;
public Student(String name, int age, int score) {
super();
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";
}
// 指定比较规则
// 按照分数进行降序排序
// this - o 升序
// o - this 降序
@Override
public int compareTo(Student o) {
return o.score - this.score;
}
}
扩展:
BlockingDeque - 阻塞式双端队列 - 允许双向进出
ConcurrentMap - 并发映射
一、概述
1. 是JDK1.5提供的一套用于应对高并发的映射,并且在并发过程中能保证线程安全
二、ConcurrentHashMap - 并发哈希映射
1. 底层基于数组+链表结构
2. 初始容量为16,加载因子是0.75,默认扩容是增加一倍
3. ConcurrentHashMap采用的是分段/桶锁机制,来保证读写效率
4. ConcurrentHashMap在后续版本中,在分段锁的基础上引入了读写锁机制:
a. 读锁:允许多个线程读,不允许线程写
b. 写锁:只允许一个线程写,不允许线程读
在JDK1.8中,为了避免锁所带来的开销,引入了CAS(Compare And Swap, 比较和交换)无锁算法。CAS需要和计算机具体的内核架构相关
6. 在JDK1.8中,ConcurrentHashMap为了提高查询效率引入了红黑树机制
7. 红黑树:
a. 本质上是一棵自平衡二叉查找树
b. 二叉查找树 - BST - Binary Search Tree
i. 可以将二叉查找树理解为二分查找的空间结构
ii. 要求左<根<右
c. 特点:
i. 所有节点非红即黑
ii. 根节点为黑
iii. 红节点的子节点一定为黑,黑节点的子节点可以为黑可以为红
iv. 最下层的叶子节点一定是黑色的空节点
v. 从根节点到任意一个叶子节点所经过的路径中的黑色节点个数要一致,既黑色节点高度是一致的
vi. 新添的节点颜色为红
d. 修正:红黑树一旦产生修正,一定是父子节点都为红
i. 涂色:叔父节点为红,那么将父节点以及叔父节点涂黑,将祖父节点涂红
ii. 左旋:叔父节点为黑,并且当前节点为右子叶,那么需要以当前节点为轴进行左旋
iii. 右旋:叔父节点为黑,并且当前节点位左子叶,那么需要以父节点为轴进行右旋
iv. 例子:红黑树的修正是一个链式反应过程
e. 红黑树的时间复杂度是O(logn)
扩展:时间复杂度
三、ConcurrentNavigableMap - 并发导航映射
1. 提供了用于截取子映射的方法
2. 本身是一个接口,所以使用的是实现类ConcurrentSkipListMap - 并发跳跃表映射
3. ConcurrentSkipListMap底层基于跳跃表来实现的
4. 跳跃表:
a. 适合于查询多的场景
b. 要求元素必须有序
c. 跳跃表允许层层提取,但是最上层的跳跃表中的元素个数至少是2个
d. 跳跃表是一个典型的以空间换时间的产物
e. 在跳跃表中,新添的元素是否提取到上层的跳跃表中,遵循"抛硬币"原则
f. 跳跃表的时间复杂度是O(logn)
package cn.tedu.concurrentmap;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;
public class ConcurrentNavigableMapDemo {
public static void main(String[] args) {
ConcurrentNavigableMap<String, Integer> map =
new ConcurrentSkipListMap<>();
map.put("d", 3);
map.put("a", 3);
map.put(