JVM8-锁及优化

线程安全

下面类由于ArrayList不是线程安全的在数组扩展的时候回导致异常
public class SafeThread {
public static List numberList = new ArrayList<>();
public static class AddToList implements Runnable{
int startNum = 0;
public AddToList(int startNum) {
this.startNum = startNum;
}
@Override
public void run() {
int count = 0;
while(count < 1000000){
numberList.add(startNum);
startNum += 2;
count++;
}
}
}
public static void main(String[] args) throws Exception{
Thread t1 = new Thread(new AddToList(0));
Thread t2 = new Thread(new AddToList(1));
t1.start();
t2.start();
while(t1.isAlive() || t2.isAlive()){
Thread.sleep(1);
}
System.out.println(numberList.size());
}
}

对象头Mark

描述对象的hash、锁信息,垃圾回收标记,年龄
指向锁记录的指针;指向monitor的指针 ;GC标记;偏向锁线程ID;

偏向锁

应用场景:大部分情况是没有竞争的情况,可以铜鼓哦偏向来提高性能;
锁偏向当前已经占有锁的线程。
将对象Mark的标记设置为偏向,并将线程ID写入对象头mark
只要没有竞争,获得偏向锁的线程,在将来进入同步块,不需要做同步
当其他线程请求相同的锁时,偏向模式结束
-XX:+UseBiaseLocking 默认启用;
-XX:BiasedLockingStartupDelay = 0;
-XX:-UseBiasedLocking 不启用偏向锁
竞争激烈的场合,不使用;
默认撑起刚开始不使用偏向锁,刚开始竞争比较激烈

轻量级锁

BacicObjectLock 嵌入在线程栈中的对象 。
包含两部分:BasicLock 对象头;指向持有本锁的对象的指针;
普通锁的处理性能不理想
如果对象没有被锁定,将对象头的mark指针保存到锁对象中,将对象头设置为指向锁的指针。
对象头的指的方向是否在线程的内存区域中,说明线程持有锁。
具体代码:

1
2
3
4
5
lock.set_displaecd_header(mark);
if(mark == (markOop) Atomic::cmpxchg_ptr(lock,obj()->mark_addr(),mark)){
TEVENT(slow_enter:release stacklock);
return ;
}

如果轻量级锁失败表示存在竞争,升级为重量级锁(常规锁)
在没有锁竞争的前提下,减少传统锁使用OS互斥产生的性能损耗;

自旋锁

空循环,等待锁。
如果线程可以很快获得锁,name可以不再os层挂起线程,让线程做几个空操作(自选)
如果同步块很长,自选失败,会降低系统性能
如果同步快很短,自选成功,节省线程挂起切换时间,提升系统性能

JVM获取锁的步骤

偏向锁可用先会尝试偏向锁。
轻量级锁可用胡先尝试轻量级锁
以上都失败,尝试自旋锁
再失败,尝试普通锁,使用os互斥量在操作系统层挂起

Java并发编程技巧

减少锁粒度

将大对象,拆成小对象,打打增加并行度,降低锁竞争。
ConcurrentHashMap
若干个Segment:Segment[] segments
Segment中维护HashEntry
put 操作时 先定位到Segment,锁定一个Segment,执行put

锁分离

根据功能进行锁分离,如:读锁 、写锁。
读多写少的情况
LinkedBlockingQueue 分为take锁和put锁

锁粗化

如果对同一个锁不停的进行请求、同步和释放,其本身也会消耗系统宝贵的资源,反而不利于性能优化。 多个同步快合并,不需要同步很快完成。

锁消除

局部变量不使用同步机制的集合
-server-XX:+DoEscapeAnalysis -XX:+ElminateLocks 逃逸分析 判断是否逃逸出局部范围

无锁

锁是悲观的操作:预期竞争存在
无锁是乐观的操作:认为竞争不存在
无锁的实现方式:
CAS(Compare And Swap);非阻塞的同步;
CAS(V,E,N); V需要更新的值,E期望当前的值,N新的值 ;返回最终值;
不出现线程的切换
AtomicInterger