java阻塞队列
1、arrayBlockingQueue数组结构、有界阻塞队列
公平非公平
先进先出对元素排序, 默认不保证访问者公平的访问队列
公平访问队列:阻塞all生产者/消费者线程,当队列可用,按阻塞顺序访问队列,先阻塞的生产者线程先往队列插入,先阻塞的消费者线程先从队列获取
公平阻塞队列ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true)
2、linkedBlockingQueue链表结构、有界阻塞队列
两个独立锁提高并发
先进先出fifo对元素排序,生产者端和消费者端分别采用独立的锁控制数据同步;默认一个类似无限大小的容量
3、priorityBlockingQueue支持优先级排序、无界阻塞队列
compareTo排序实现优先
默认是自然顺序,可自定义实现compareTo来指定元素进行排序规则,或初始化时指定构造参数Comparator对元素进行排序,不能保证同优先级元素的顺序,像redis的zset同分数一样
4、delayQueue优先级队列实现的无界阻塞队列
缓存失效,定时任务,支持延时获取元素
使用priorityQueue实现,队列中元素要实现delayed接口,创建元素可指定多久才能从队列获取当前元素
5、synchronousQueue不存储元素的阻塞队列
不存储数据,传递数据,吞吐量高于前两者
每个put要等待一个take操作否则不能添加元素,把生产者线程处理的数据直接传递给消费者线程
6、linkedTransferQueue链表结构组成的无界阻塞队列
transferQueue队列,多了tryTransfer和transfer方法
transfer如当前消费者正在等待接收元素(消费者使用take或时间限制poll),该方法把生产者传入的消息立刻transfer(船速)给消费者,如果没有消费者在等待接收则将元素放在队列tail尾节点并等该元素被消费才返回
tryTransfer试探生产者传入的元素是否能直接传给消费者,如无消费者等待接收则返false
tryTransfer(E e,long timeout,TimeUnit unit)试图把生产者传入的元素直接传给消费者,如没有消费者消费该元素则等待指定的时间再返回,如果超时返回false,未超时消费了返回true
7、linkedBlockingDeque链表结构组成的双向阻塞队列
两端插入和移除元素,addFirst addLast offerFirst offerLast peekFirst peekLast,first结尾的表示插入、获取peek或移除双端队列的第一个元素,last则是插入获取移除最后一个元素
countDownLatch线程计数器
位于java.util.concurrent包下,实现类似计数器的功能
CyclicBarrier:一组线程等待至某个状态后同时执行
await挂起当前线程,直到all线程到达barrier状态再同时执行后继任务
await(long timeout,TimeUnit unit)等待至指定时间还让线程没有到达barrier状态就直接让到达barrier的线程执行后继任务
semaphore信号量
控制同时访问的线程个数,acquire获取许可无许可则等待,release释放许可
acquire(int permits)获取许可,acquire,release释放许可,release(int permites)释放n个,这四个方法都会被阻塞
boolean tryAcquire尝试获取若成功返true,失败返false
boolean tryAcquire(long timeout,TimeUnit unit)尝试指定时间获取许可,成功返true失败false
boolean tryAcquire(int permits)、boolean tryAcquire(int permits,long timeout,TimeUnit unit)
availablePermits得到可用的许可数目
countDownLatch和CyclicBarrier实现线程间等待,侧重点不同,countDownLatch一般某个线程等待若干其他线程执行完任务之后才执行;cyclicBarrier用于一组线程互相等待至某个状态,然后同时执行,cyclicBarrier可重用
volatile变量可见性、禁止重排序,不会被缓存在寄存器或者其他处理器不可见的地方
所有线程可见,当一个线程修改了变量的值,新值对于其他线程可立即获取
比synchronize更轻量级的同步锁,访问volatile变量时不会加锁
非volatile变量读写,每个线程先从内内存拷贝变量到cpu缓存,如果有多个cpu每个线程可能在不同cpu上被处理,每个线程拷贝到不同cpu cache中,而声明变量的volatile,jvm每次读取从内存中读、跳过cpu cache
threadLocal
threadlocal线程本地储存,提供线程内部的局部变量,在线程生命周期内起作用,减少同一个线程内多个函数或组件间公共变量传递的复杂度
threadlocalMap每个线程都有一个自己的threadLocalMap类对象,可将线程自己的对象保持到其中
将公用的threadLock静态实例作为key,不同对象的引用保存到不同线程的threadLocalMap中,然后在线程执行的各处通过这个静态threadLocal实例的get取得自己线程保存的那个对象
get获取threadLocal在当前线程保存的变量副本,set设置当前线程中变量的副本
remove移除当前线程中变量的副本,initialValue延迟加载,在使用时进行重写
https://www.jianshu.com/p/98b68c97df9b
https://www.jianshu.com/p/6fc3bba12f38
https://www.jianshu.com/p/3c5d7f09dfbd
上面三篇博客大概的介绍很可以,我把我的重点展示一下
//ThreadLocalMap构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//内部成员数组,INITIAL_CAPACITY值为16的常量
table = new Entry[INITIAL_CAPACITY];
//位运算,结果与取模相同,计算出需要存放的位置
//threadLocalHashCode比较有趣
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
//ThreadLocalMap中set方法。
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
//获取索引值,这个地方是比较特别的地方
int i = key.threadLocalHashCode & (len-1);
//遍历tab如果已经存在则更新值
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//如果上面没有遍历成功则创建新值
tab[i] = new Entry(key, value);
int sz = ++size;
//满足条件数组扩容x2
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
//ThreadLocal中get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//this不是t
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
//ThreadLocalMap中getEntry方法
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
consurrentHashMap并发
分段锁,减小锁粒度:缩小锁定对象的范围,提高并发
由segment数组结构和hashentry数组结构组成,segment是可重入锁,hashentry储存键值对;segment类似hashmap数组链表结构,一个segment含一个hashentry数组,每个hashentry是一个链表结构的元素
1.8改进:
添加元素首先据hashcode得到该表应放在哪个段中,该段加锁完成put
抢占式调度
每条线程执行的时间,线程切换由系统控制
jvm线程调度:按照优先级分配CPU时间片运行
协同式调度
某一线程执行完成后主动通知系统切换到另一线程上执行,执行时间由线程本身控制,线程切换可预知,不存在多线程同步问题;如果一个线程编写有问题则堵塞可能导致系统崩溃
线程让出cpu
- 当前运行线程主动放弃cpu、jvm暂时放弃cpu
- 当前运行线程因为某些原因进入阻塞状态:阻塞在I/O上
- 当前运行线程结束
进程调度算法
优先调度
1、FCFS先来先服务:算法简单,基本上公平
作业调度:每次调度从后备作业队列选择一个或多个最先进入该队列的作业,调入内存分配资源创建进程,放入就绪队列
进程调度采用fcfs,每次调度从就绪队列选择一个最先进入的进程,分配处理机,运行到完成或因某事件阻塞放弃处理机
2、短作业优先调度算法
短作业优先SJF从后备队列选*个估计运行时间最短的作业,调入内存运行