Current并发包(一)
是JDK1.5提供的并发包
Concurrent包主要包含了5块内容
BlockingQueue
- 阻塞式队列
ConcurrentMap
- 并发映射
ExecutorService
-执行器服务
Lock -
原子性操作
BlockingQueue - 阻塞式队列
本身是一个队列 -用于对线程进行生产和消费
满足队列FIFO的特点
阻塞队列都是有界的,所有的阻塞式队列的大小是固定的
如果队列已满,则添加的元素会被阻塞,直到队列中有元素被取出
如果队列为空,则获取元素的线程会被阻塞,直到队列中被添加元素
阻塞队列适合于生产消费模型
阻塞队列中要求元素非空
实现类
ArrayBlockingQueue
- 阻塞式顺序队列底层是基于数组来进行存储
在使用的时候需要指定容量,并且容量指定之后是不可变的
LinkedBlockingQueue - 阻塞式链式队列
底层是基于链表实现
容量可以不指定,默认容量:Integer.MAX_VALUE,即2^31-1
一般不会向一个队列中添加21亿个值,认为无界
PriorityBlockingQueue
- 具有优先级的阻塞式队列在使用的时候如果不指定容量,则默认容量为11
在遍历元素的时候是有序的
要求元素必须实现Comparable接口
实现的排序称之为是自然排序
SynchronousQueue
- 同步队列在使用的时候不需要指定容量。默认容量是1并且只能为1
扩展
BlockingDeque
阻塞式双向队列 - 允许从两端放或者拿元素
抛出异常 | 返回值 | 长期阻塞 | 定时阻塞 | |
---|---|---|---|---|
放 | add | offer - false | put | offer |
拿 | remove | poll - null | take | poll |
BlockingQueueDemo.java
添加元素
queue.add("f");
队列已满,抛出异常
queue.offer("g");
队列已满,返回false
queue.put("h")
队列已满,阻塞(永久)
queue.offer("r", 5, TimeUnit.SECONDS);
队列已满,定时阻塞
package com.wiscom.queue; public class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(5); // 添加元素 queue.add("a"); queue.add("b"); queue.add("c"); queue.add("d"); queue.add("e"); // 队列已满,抛出异常 // queue.add("f"); // 队列已满,返回false // boolean b = queue.offer("g"); // 队列已满,产生阻塞 // queue.put("h"); // 队列为空,定时阻塞 queue.offer("r", 5, TimeUnit.SECONDS); System.out.println(queue); } }
BlockingQueueDemo2.java
取出元素
queue.remove()
队列为空,抛出异常
queue.poll()
队列为空,返回null
queue.take()
队列为空,阻塞(永久)
queue.poll(5, TimeUnit.SECONDS)
队列为空,定时阻塞
package com.wiscom.queue; public class BlockingQueueDemo2 { public static void main(String[] args) throws InterruptedException { // ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(5); 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)); } }
package com.wiscom.queue; public class PriorityBlockingQueueDemo { public static void main(String[] args) throws InterruptedException { // PriorityBlockingQueue<String> queue = new PriorityBlockingQueue<>(7); // // queue.put("g"); // queue.put("a"); // queue.put("r"); // queue.put("h"); // queue.put("d"); // queue.put("t"); // queue.put("u"); PriorityBlockingQueue<Student> queue = new PriorityBlockingQueue<>(5); queue.put(new Student("曹洋", 80, 59)); queue.put(new Student("李帅", 30, 61)); queue.put(new Student("孟祥冰", 40, 19)); queue.put(new Student("肖旭伟", 40, 70)); queue.put(new Student("谷丰硕", 18, 30)); // for (int i = 0; i < 7; i++) { // System.out.println(queue.take()); // } for (Student s : queue) { System.out.println(s); } } } class Student implements Comparable<Student> { private String name; private int age; private int score; public Student(String name, int age, int score) { 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 this.age - o.age; } }
ConcurrentMap - 并发映射
本质上是一个Map,依然以键值对结构来存储数据
ConcurrentMap
提供了并发并且安全的方式来读写数据
ConcurrentHashMap - 并发哈希映射
Hashtable
对外界提供的方法都是同步非静态方法的锁对象
同步非静态方法的锁对象是this --— this 当前对象 --— Hashtable是以当前对象作为锁对象
底层是基于数组+链表存储,默认初始容量为16
默认加载因子是0.75
每次扩容默认增加一倍
ConcurrentHashMap
是一个异步式线程安全的映射
引入了分段 (桶)锁 机制来解决
Hashtable
所带来的效率降低的问题在后续JDK版本中,
ConcurrentHashMap
在分段锁的基础上引入了读写锁来提高效率读锁:允许多个线程读,不允许线程写
写锁:只能允许一个线程写,不允许线程读
在JDK1.8中
ConcurrentHashMap
引入了CAS(Compare And Swap,比较和交换)无锁算法无锁算法保证异步线程安全
CAS算法需要与具体的内核结合
目前几乎所有的内核架构都是支持CAS
CAS的语义
我认为V的值应该是A,如果是,那么将V的值更新为B
否则不修改并告诉V的值实际为多少
V:内存值
A:旧的预期值
B:新的预期值