1. 容器的实现
物理结构上,就是 数组+链表
利用数组+链表可以实现各种各样的容器
2. 容器的分类
容器分两大类,Collection和 Map,Collection有分为 List Set Queue
3. 容器的演变
3.1 Map
Hashtable-----> HashMap-----> synchronizedHashMap----->ConcurrentHashMap(写入效率慢,读快)
- Hashtable 实现Map接口,是线程安全的,Hashtable 的方法都是synchronized方法。
- HashMap 实现Map接口,是线程不安全的。由于大部分的是时候都是单线程的,所以出现了HashMap。
- synchronizdeHashMap,是Collections的内部类,通过以下方式获取。
Map<String, String> stringStringMap = Collections.synchronizedMap(new HashMap<String, String>());
和Hashtable差不多,要说区别就是Hashtable都是synchronized方法;而synchronizedMap则是 synchronized(object)块。
- ConcurrentMap
3.2 Collection
voctor ------> list----->set ------->queue
Queue 和list的区别
- 对线程友好的API offer peek poll
- BlockingQueue put tack 阻塞
一些设计原则:但一定一实际情况数据量,通过压测确认用那种方法。
单线程就考虑HashMap list等
多线程 锁代码块执行速度快,代码量少 考虑 ConcurrentHashMap queue
多线程,锁的代码块代码量大,执行慢,synchronized
3.2 常用容器
3.2.1 Map
concurrent 并发的,一致的,同时发生的,并存的
ConcurrentMap 继承Map,ConcurrentHashMap实现ConcurrentMap
ConcurrentHashMap,无序的,cas实现线程安全,key value不能为空
ConcurrentSkipListMap 跳表
没有ConcurrentTreeMap,实现很困难,用ConcurrentSkipListMap实现排序,cas线程安全,key,value不能为空
跳表原理:
HashMap TreeMap LinkedHashMap
3.2.2 List
CopyOnWriteArrayList. 写时复制。 适用场景 读多,写少。 回忆一下读写锁。读共享锁,写排他锁
CopyOnWriteSet
SynchronizedList
3.2.3 Queue
Queue strs = new ConcurrentLinkedQueue<>();
ConcurrentLinkedQueue继承了Collection类,新增了一下方法。
offer() 添加,根据返回判断是否加满。
add() 添加,内部调用了offer方法,满了会报错。
poll() 获取,取完删除。
peek()获取。
LinkedBlockingQueue 无界的,可以一直添加,直到内存溢出。实现BlockingQueue
ArrayBlockingQueue 有界的,指定固定的值。在创建的时候需要指定容量。实现BlockingQueue
Queue queue = new ArrayBlockingQueue<>(8);
add元素的时候,当add的长度超过指定的容量会报错。
而offer方法不会,但是offer方法只能添加到指定长度,需要通过返回值判断是否加入成功或者添加满。
提供方法:put 和take
put() 阻塞加,如果队列满了就会阻塞等待。
take()阻塞取,如果对列空了就会阻塞等待。
天然的生产者 消费者模式。
public class c14_BlockingQueue3 {
public static void main(String[] args) {
BlockingQueue<String> queue = new ArrayBlockingQueue<>(8);
for (int j = 0; j < 5; j++) {
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
queue.put("元素"+i); //如果满了就会等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"provider").start();
}
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
while(true) {
String take = queue.take();//如果空了就会等待
System.out.println(take);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"consumer").start();
}
}
}
按紧迫程度排序
public class c14_DelayQueue {
public static void main(String[] args) {
c14_DelayQueue c14_delayQueue = new c14_DelayQueue();
BlockingQueue queue = new DelayQueue();
long now = System.currentTimeMillis();
MyTask t1 = c14_delayQueue.new MyTask("t1", now +5000);
MyTask t2 = c14_delayQueue.new MyTask("t2", now +2000);
MyTask t3 = c14_delayQueue.new MyTask("t3", now +1500);
MyTask t4 = c14_delayQueue.new MyTask("t4", now +2500);
MyTask t5 = c14_delayQueue.new MyTask("t5", now +500);
try {
queue.put(t1);
queue.put(t2);
queue.put(t3);
queue.put(t4);
queue.put(t5);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 5; i++) {
try {
MyTask take = (MyTask)queue.take();
System.out.println(take.name+"---"+take.myTimes);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 实现delayed接口,实现比较方法
*/
public class MyTask implements Delayed {
private String name;
private long myTimes;
public MyTask(String name, long myTimes) {
this.name = name;
this.myTimes = myTimes;
}
@Override
public long getDelay(TimeUnit unit) {
//返回的是延时时间
long l = myTimes - System.currentTimeMillis();
return unit.convert(l, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
if (this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)) {
return 1;
} else if (this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS)) {
return -1;
}
return 0;
}
}
}
需要实现一个比较器,Comparator。
public class c14_PriorityQueue {
public static void main(String[] args) {
c14_PriorityQueue c14_priorityQueue = new c14_PriorityQueue();
Queue<User> queue = new PriorityQueue<User>(5, new Comparator<User>() {
@Override
public int compare(User user1, User user2) {
if(user1.age>user2.age){
return 1;
}else if (user1.age<user2.age){
return -1;
}
return 0;
}
});
queue.offer(c14_priorityQueue.new User("张三",30));
queue.offer(c14_priorityQueue.new User("李四",25));
queue.offer(c14_priorityQueue.new User("王五",24));
queue.offer(c14_priorityQueue.new User("赵六",56));
queue.offer(c14_priorityQueue.new User("钱七",35));
for (int i = 0; i < 5; i++) {
System.out.println(queue.poll());
}
}
public class User{
private String username;
private int age;
public User(String username,int age){
this.username=username;
this.age=age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", age=" + age +
'}';
}
}
}
SynchronusQueue 是一个容量为0的队列,无法装东西,手把手的从一个线程传递数据到另一个线程。
调用add方法会报错
put一个元素后就会阻塞,直到take这个元素。
public class c14_SynchronusQueue {
public static void main(String[] args) {
c14_SynchronusQueue c14_delayQueue = new c14_SynchronusQueue();
BlockingQueue<String> queue = new SynchronousQueue<>();
new Thread(()->{
while (true){
String take = null;
try {
take = queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(take);
}
}).start();
for (int i = 0; i < 10; i++) {
try {
queue.put("a"+i);
System.out.println("put"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
调用transfer方法立刻阻塞。一直等到元素被消费(take);
调用put方法不会阻塞。只有当queue满了才会阻塞。当然LinkedTransferQueue是无界的。当内存占满才会阻塞。
public class c14_TransforQueue {
public static void main(String[] args) {
c14_TransforQueue c14_delayQueue = new c14_TransforQueue();
LinkedTransferQueue<String> queue = new LinkedTransferQueue<>();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
String take = queue.take();
System.out.println(take);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
for (int i = 0; i < 10; i++) {
try {
queue.transfer("b"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
总结:
queue
添加方法
add ,添加满之后会报错。
offer,添加不会报错,可以根据返回的true或false判断是否加满
获取方法
poll,获取一个,删除一个。
peek,只获取不删除。
BlockingQueue 阻塞队列
put ,put满了之后会阻塞
take,队列空了会阻塞
LinkedTransferQueue
transfer 调用transfer阻塞,直到take。
SynchronusQueue 同步队列
手把手的同步。一个put 对应一个take。