高并发知识点
2018-03-23 本文已影响189人
Kiss石头君
CPU多级缓存
1. 缓存一致性(MSIE)
MSIE:用于保证多个cpu cache之间缓存共享数据的一致。
image
M:被修改 S:共享 I:无效 E:独享
2. 乱序执行优化
--处理器为提高运算速度而做出违背代码原有顺序的优化。
3. JVM内存模型
image--每个线程同时访问同一个对象的同一个方法,他们都会访问这个对象的成员变量,但是他们所拥有的是这个对象的私有拷贝。
image
image image image image
4. 并发的优势与风险
imageAtomic类
- AtomicXXX
底层通过CAS(compareAndSwapInt)实现乐观锁,dowhile循环判断是否和底层记录的值一样
- LongAdder
高并发情况下效率高
- AtomicReference
private static AtomicReference<Integer> count = new AtomicReference<>(0);
count.compareAndSet(0, 2);
count.compareAndSet(0, 1);
count.compareAndSet(1, 3);
count.compareAndSet(2, 4);
count.compareAndSet(3, 5);
log.info("count:{}",count.get());//4
- AtomicIntegerFiledUpdater
@Getter
public volatile int count = 100;
private static AtomicExample4 example4 = new AtomicExample4();
public static void main(String[] args) {
//实例指定类名和字段,字段必须通过volatile修饰,实现乐观锁
AtomicIntegerFieldUpdater<AtomicExample4> updater = AtomicIntegerFieldUpdater.newUpdater(AtomicExample4.class, "count");
if (updater.compareAndSet(example4, 100, 120)) {
log.info("update{}",example4.getCount());
}
}
- AtomicStampedReference解决ABA问题,添加版本号
Synchronized与Lock
imageSynchronized
- 可见性:解锁前,把共享变量刷新到主内存;加锁时,清空工作内存的值,并从向内存中获取最新值
Volatile
- 通过加入内存屏障,和禁止重排序优化,来实现可见性
- 对Volatile变量修改时,强制刷入主内存;读取时,强制获取主内存的值,在cup指令级别加入屏障保证顺序
- 修饰的变量不适合写操作,多于获取当前状态和DoubleCheck
happens-before八条原则
- 程序次序规则:单线程按书写顺序进行
- 锁定规则:先unlock,后lock
- volatile变量规则:先写后读
- 传递规则:A->B,B->C,A->C
- 线程启动规则:start()
- 线程中断规则:interrupt()
- 线程终结规则:join()
- 对象终结规则:finalize()
线程安全性
- 原子性:提供互斥访问
- 可见性:一个线程对主内存的修改,可以及时的被其他线程观察到
- 有序性:无法从hb原则推导出来,则虚拟机会进行重排序
发布对象
- 发布对象:使一个对象能够被当前范围之外的代码所使用
- 对象溢出:当一个对象还没有构造完成时,就被其他线程所见
安全发布对象
- 在静态初始化函数中初始化一个对象引用
- 将对象引用保存到volatile类型域或者AtomicReference对象中
- 将对象的引用保存到某个正确构造对象的final类型域中
- 将对象的引用保存到一个由锁保护的域中
final
imageCollections.unModifiableMap(map)禁止修改数据,guava里的ImmutableList.of(list)
ThreadLocal线程封闭,规避并发
- connection
SDF和JodaTime
- SDF不是线程安全
- 使用JodaTime
private static DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyyMMdd");
同步容器
CopyOnWriteArrayList
- 适合读多写少
- 读写分离,写的时候加ReentrantLock
- 开辟空间,解决并发冲突
CopyOnWriteArraySet
ConcurrentSkipListSet
ConcurrentHashMap
- 读取效率高
ConcurrentSkipListMap
- 高并发效率高,key有序
J.U.C包
imageAQS(AbstractQueuedSynchronizer)
CountDownLatch 闭锁
- 原子计数器
- countDown()向下计数
- await()结束计数,传入参数可提前结束
Semapphore 信号量
- 维护了当前访问个数
- acquire()获取许可,可获取多个
- release()释放许可,可释放多个
- boolean tryAcquire()尝试获得许可
CyclicBarrier 多线程闭锁
ReentrantLock
- 可重入性
- 基于JDK实现(Synchronize基于JVM)
- 指定公平锁和非公平锁(先等待先获得)
- 可选择分组唤醒(Synchronize随机唤醒)
- 可中断lock.lockInterruptibly()
- lock()unlock()加锁解锁,unlock放在finally块
ReentrantReadWriteLock 悲观锁
- 可获取读锁和写锁
- 获取写锁时,必须没有任何锁保持
StampedLock
class Point {
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) { // an exclusively locked method
long stamp = sl.writeLock();
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
//下面看看乐观读锁案例
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //获得一个乐观读锁
double currentX = x, currentY = y; //将两个字段读入本地局部变量
if (!sl.validate(stamp)) { //检查发出乐观读锁后同时是否有其他写锁发生?
stamp = sl.readLock(); //如果没有,我们再次获得一个读悲观锁
try {
currentX = x; // 将两个字段读入本地局部变量
currentY = y; // 将两个字段读入本地局部变量
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
//下面是悲观读锁案例
void moveIfAtOrigin(double newX, double newY) { // upgrade
// Could instead start with optimistic, not read mode
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) { //循环,检查当前状态是否符合
long ws = sl.tryConvertToWriteLock(stamp); //将读锁转为写锁
if (ws != 0L) { //这是确认转为写锁是否成功
stamp = ws; //如果成功 替换票据
x = newX; //进行状态改变
y = newY; //进行状态改变
break;
} else { //如果不能成功转换为写锁
sl.unlockRead(stamp); //我们显式释放读锁
stamp = sl.writeLock(); //显式直接进行写锁 然后再通过循环再试
}
}
} finally {
sl.unlock(stamp); //释放读锁或写锁
}
}
}
使用:
long stamp = lock.writeLock();
try{
count++;
}finally{
lock.unlock(stamp);
}
Condition 控制AQS等待队列
- await()等待 singal()唤醒
FutureTask
- 继承了Future和Runnable
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
log.info("do something in callable");
Thread.sleep(5000);
return "Done";
}
});
new Thread(futureTask).start();
log.info("do something in main");
Thread.sleep(1000);
String result = futureTask.get();
log.info("result:{}", result);
Fork/Join框架
- 把大任务分解成若干小任务执行
- mapperreduce思想
- 工作窃取算法:双端队列,先执行完毕的线程,会从其他尾部开始执行任务,进行充分并行计算
- recursive 递归
BlockingQueue
- ArrayBlockingQueue有界队列,先进先出
- DelayQueue有序
- LinkedBlockingQueue链表结构,先进先出
- PriorityBlockingQueue有序
- SynchronousQueue仅容纳一个元素