目录
day6
一,java 容器
物理的存储只有数组和链表
二,MAP
Vector,HashTbale jkd1.0 设计理念就是同步线程安全的,所以每个方法都是加了锁的synchronized。但多数的时候都是在单线程工作,这种情况下是不需要线程安全的。
HashMap 完全没有加锁,新的Map容器比HashTbale好用,但是它又没有加锁,怎么让它既支持锁的环境又支持非锁的环境呢?又添加了一个Collections的工具类,其中有一个synchronizedMap会把HashMap变成有锁的版本。
由于设计上的缺陷Vector,HashTbale 基本不用。
HashMap 与HashTbale 区别在于,HashMap 内部声明了一个类用于加锁,虽然也是用的synchronized,但是锁的粒度上更细,效率上略高于HashTbale 。
后期又添加了一个效率更高的Map:ConcurrentHashMap。
性能测试
java Map 读写性能测试_解决问题no解决代码问题的博客-CSDN博客
1,linkedHashMap
线程安全,无序的
是在HashMap的基础上添加了链表,加快了访问便利的效率;
2,TreeMap
线程不安全,无序的
红黑树,本身是排序的,查找的时候效率比较高
是为了实现拍好顺序之后查询快速,插入的效率也不是特别低
3,ConcurrentSkipListMap
线程安全,有序,跳表
由于在红黑树上实现用C+操作比较复杂,用跳表代替了
底层是链表,找到关键元素形成一个链表,减少的是检索的次数,同时CAS的实现又比红黑树结构的容易很多。
三,Queue
阻塞队列,为了高并发做准备
一端进数据,一端取数据,取的顺序不一定是进数据的顺序,需要看具体实现
PriorityQueue : 排序的队列
DelayQueue : 按任务到期的顺序,优先快过期的任务
Deque :双端队列
1,从Vector 到 Queue
多线程下,如果有重复的数据,多考虑Queue而不是List。
双方的区别就是Queue添加了很多对线程友好的API offer prrk poll
BlockingQueue put take 阻塞
2,CopyOnWriteArrayList
场景:写的少,读的多
写时复制
当需要往容器种添加元素的时候,把底层的array复制出来长度+1并把要添加的元素放在末尾,最后把读的引用指向新的array。(需要避免每次使用size)
只有add需要加锁
3,ConcurrentLinkedQueue
全是线程安全的操作
offer (add) 返回一个boolean,add加不进去会返回异常
peek 取 并不会removes
poll 取 会removes
4,BlockingQueue
4.1 LinkedBlockingQueue
阻塞队列,让线程实现自动的阻塞
无界(无界是相对的,size方法返回int类型,最大长度对应也就是int的最大值):链表实现的,没有长度限制,能添加到内存溢出
put :添加数据,一定要添加进去,如果容器满了就阻塞
take :获取数据,一定要获取数据,如果容器是null的情况就阻塞
天生就是消费者生产者的模型
阻塞的底层是用的LockSupport.park(this);
4.2,ArrayBlockingQueue
有界:可以指定容器size
5,DelayQueue
实现在时间上的排序
场景:按时间进行任务调度
package com.example.demo.thread.container;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* DelayQueue是用的PriorityQueue实现的
* 加了阻塞相关的API
*/
public class T4_DelayQueue {
static DelayQueue<MyTask> queue = new DelayQueue();
static class MyTask implements Delayed{
String name;
long runningTime;
MyTask(String name, long runningTime){
this.name = name;
this.runningTime = runningTime;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(runningTime - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
//毫秒,这个让我联想到了BigDecimal
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;
}
@Override
public String toString() {
return name+" "+runningTime;
}
}
public static void main(String[] args) throws InterruptedException {
long now = System.currentTimeMillis();
MyTask t1 = new MyTask("t1",now+1000);
MyTask t2 = new MyTask("t2",now+1500);
MyTask t3 = new MyTask("t3",now+500);
MyTask t4 = new MyTask("t4",now+1500);
queue.put(t1);
queue.put(t2);
queue.put(t3);
queue.put(t4);
System.out.println("queue = " + queue);
for (int i = 0; i < 4; i++) {
System.out.println(queue.take());
}
}
}
Console:
queue = [t3 1648998055494, t2 1648998056494, t1 1648998055994, t4 1648998056494]
t3 1648998055494
t1 1648998055994
t4 1648998056494
t2 1648998056494
6,SynchronousQueue
容量为0,在两个线程之间实现传递数据
没人在前面等着,就无法往queue里面添加
阻塞等待消费者消费
package com.example.demo.thread.container;
import java.util.concurrent.SynchronousQueue;
public class T6_SynchronousQueue {
public static void main(String[] args) throws InterruptedException {
SynchronousQueue<String> queue = new SynchronousQueue<>();
//没人在前面等着,就无法往queue里面添加
// queue.put("jjj");
// System.out.println(queue.size());
new Thread(()->{
try {
System.out.println("t1 "+queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();;
queue.put("jjj");//阻塞等待消费者消费
// queue.add("aaa");//java.lang.IllegalStateException: Queue full
System.out.println(queue.size());
}
}
7,TransferQueue
给线程传递数据,不同与SynchronousQueue只能传递一个的是,可也传递好多个
如果用put跟其他就没什么不同了
transfer : 装完 阻塞等着别人取走(等结果) 在执行,必须先启动消费者线程
场景:等结果,付账有结果之后,才走下一步返结果。手机银行转账的场景,有读秒,然后返回是否成功。
交替打印T3
package com.example.demo.thread.t5mst;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
import java.util.concurrent.locks.LockSupport;
public class T3TransferQueue {
static TransferQueue<String> queue = new LinkedTransferQueue<>();
public static void main(String[] args) {
String[] strs = new String[]{"A","B","C","D","E"};
new Thread(()->{
for (;;)
try {
System.err.println(Thread.currentThread().getName() + " " + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1").start();
new Thread(()->{
for (int i = 0; i < strs.length ; i++) {
System.err.println(Thread.currentThread().getName() + " " + strs[i]);
try {
queue.transfer(i+"");
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t2").start();
}
}