目录
1. 概述:
是JDK1.5提供的一套用于高并发和保证线程安全的包:
- java.until.concurrent---高并发
- java.until.concurrent.lock 线程锁
- java.until.concurrent.atomic 原子性
2. 5套接口:
2.1 Blockingqueue---阻塞式双向队列
特点:
- 阻塞 ---FIFO 先进先出
- 这个队列必须有界---容量指定之后,不可变
- 若此时队列为空,视图获取元素的线程会被阻塞反之,试图放入元素的线程就会被阻塞
public interface BlockingQueue<E> extends Queue<E>
实现类:
1.ArrayBlockingQueue---阻塞是顺序队列
a.底层是基于数组实现---指定大小,大小不可变
b.若此时返回值为null,证明当前队列为空
阻塞是队列,添加元素null,会抛出空指针异常
使用场景:生产消费模型
c.重要方法:
抛出异常 | 返回值 | 永久阻塞 | 定时阻塞 | |
添加 | add | offer--true/false | put | offer |
获取 | remove | poll--null | take | poll |
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class ArrayBlocakingDemo01 {
public static void main(String[] args) throws InterruptedException {
//指定底层数组容量
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(3);
//1.添加元素
queue.add(null);
/* queue.add("B");
queue.add("C");*/
//队列以满 抛出异常
/*queue.add("C");*/
//队列已满,返回false
//boolean b = queue.offer("b");
//System.out.println(b);
//队列已满 阻塞--若没有获取队列元素就会永久阻塞
//queue.put("a");
//定时阻塞
boolean b = queue.offer("d",5, TimeUnit.SECONDS);
System.out.println(queue);
/**
* 获取元素
*/
//此时队列为空,抛出异常
//System.out.println(queue.remove());
//此时队列为空,返回null
//System.out.println(queue.poll());
//此时队列为空,没有元素 永久阻塞
//System.out.println(queue.take());
//定时阻塞
//System.out.println(queue.poll(5,TimeUnit.SECONDS));
}
}
2.LinkedBlockingQueue---链式阻塞式队列
a.底层是基于节点实现---有界的
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
创建该队列的时候,可以指定也可以不指定大小,指定的话,大小不可变;反之,则默认值为Integer.MAX_VALUE(0x7fffffff);一般认为该队列无界
永久阻塞 | 定时阻塞 |
put | offer |
take | poll |
import java.util.concurrent.LinkedBlockingQueue;
public class LinkedBlocakingDemo01 {
public static void main(String[] args) throws InterruptedException {
//指定底层数组容量
LinkedBlockingQueue queue = new LinkedBlockingQueue(3);
//1.添加元素
queue.add("a");
queue.add("B");
queue.add("C");
//队列以满 抛出异常
/*queue.add("C");*/
//队列已满,返回false
boolean b = queue.offer("b");
//System.out.println(b);
//队列已满 阻塞--若没有获取队列元素就会永久阻塞
queue.put("a");
//定时阻塞
/* boolean b = queue.offer("d",5, TimeUnit.SECONDS);*/
System.out.println(queue);
/**
* 获取元素
*/
//此时队列为空,抛出异常
//System.out.println(queue.remove());
//此时队列为空,返回null
//System.out.println(queue.poll());
//此时队列为空,没有元素 永久阻塞
//System.out.println(queue.take());
//定时阻塞
//System.out.println(queue.poll(5,TimeUnit.SECONDS));
}
}
3.priorityBlockingQueue---具有优先级
a. private static final int DEFAULT_INITIAL_CAPACITY = 11;
在使用的时候可以指定的大小,也可以不指定,初始大小为11
b.private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
此时容量用满之后,最大容量是Integer.MAX_VALUE - 8
c.private transient Object[] queue; 底层靠数组存储元素
d.在遍历队列的时候,会将放入队列的元素进行自然排序,增强for循环会顺着输入顺序打印。
e.若当前队列存储对象,进行排序,会抛出类型转换异常:需要当前对象必须实现一个Comparable接口; 若遍历元素,使用迭代遍历不能保证顺序
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
public class PriorityBlocakingDemo01 {
public static void main(String[] args) throws InterruptedException {
//指定底层数组容量
PriorityBlockingQueue<String> queue = new PriorityBlockingQueue();
//PriorityBlockingQueue<Stu> queue = new PriorityBlockingQueue();
queue.put("a");
queue.put("b");
queue.put("e");
queue.put("c");
queue.put("d");
/*queue.add(new Stu("亚洲舞王赵四",18,80));
queue.add(new Stu("战神王老七",19,75));*/
/* for (int i=0;i<5;i++){
System.out.println(queue.take());
}*/
for (String s:queue) {
System.out.println(s);
}
}
}
class Stu implements Comparable<Stu>{
private String name;
private int age;
private int score;
public Stu() { }
public Stu(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public String toString() {
return "Stu{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
@Override
public int compareTo(Stu o) {
return this.age-o.age;
}
}
4.SynchonousQueue---同步时队列
再定义的时候,根本不需要指定容量,因为容量为1
会把这个队列作为汇合点
2.2 concurrentMap --- 并发映射
1.是jdk1.5提供的用于保证高并发和线程安全的映射机制
2.hashmap---异步式线程不安全映射--->数组+链表
3.ConcurrentHashMap---并发哈希映射--->底层基于数组+链表存储数据,数组的每一个位置都是桶(bucket)结构,每一个桶中维系一个链表
4.默认初始容量是16(指的是数组的初始长度,并不代表当前映射只能存储16个元素,加载因子0.75--》已用桶的个数/总的桶的个数>加载因子,就会扩容(
private static final int tableSizeFor(int c) { int n = c - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
),每次会扩容1倍,扩容之后,会将元素重新分布)
最终的容量一定是2^n,最大容量是2^30
5. 1).在1.8之前底层采用的是分桶锁机制(分段锁),在此基础上添加了读写锁(1.7出现)
读锁:允许多个线程的读,不允许线程的写
写锁:只允许一个线程写,不允许读
官方解释:我认为V的值应该是A,如果是,那么此时将V 的值更新为B,否则不修改并告诉V实 际值是多少
CAS在执行过程中如果被打断,就会从头开始执行
3).红黑树
1.所有的节点颜色非红即黑
2.根节点必须是黑色
3.红色的子节点一定是黑色,黑节点的子节点可以是黑的也可以是红的
4.最底层的叶子节点一定时空的---nil
5.从根节点到任意一个叶子节点经过的黑节点个数一定相同
6.新添加的节点颜色必须是红色
修正:
1.涂色:当前节点为红父节点,并且叔父节点为红,那么就需要将父节点以及叔父节点涂黑,将祖父节点涂红
2.左旋:当前节点为红,父节点为红,叔父叔父节点为黑
3.右旋:当前节点为红父节点也为红,叔父节点为黑并且当前节点为左子叶,就会按照父级节点为轴,进行右旋
4.当桶中元素超过8个,会转换成一个红黑树结构;如果节点个数不足7,会重新扭转成一个链式结构,这个过程称之为树化