JAVA锁
JAVA中锁的概念
- 自旋锁: 是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环.
- 乐观锁:假定没有冲突,在修改数据时如果发现数据和之前获取的不一致,则读取最新数据,修改后重试修改.
- 悲观锁:假定会发生并发冲突,同步所有对数据的相关操作,从读数据开始上锁(synchronize)
- 独享锁:给资源加上写锁,线程可以修改资源,其他线程不能再加锁;(写)
- 共享锁:给资源加上读锁后,只能读,不能改,其他线程也只能加读锁,不能加写锁.
- 可重入锁、不可重入锁:线程拿到一把锁之后,可以自由进入同一把锁所同步的其他代码.(对于同一个线程是否可重复加锁)
//可重入锁
private final static Lock lock = new ReentrantLock();
lock.lock();//第一次加锁
lock.lock();//第二次加锁(本次加锁成功,则为可重入锁,否则为不可重入锁)
lock.unlock();
lock.unlock();
- 公平锁、非公平锁:能严格保证先来后到顺序的锁,为公平锁
synchronized关键字
synchronized简介
- 用于实例方法、静态方法时,隐式指定锁对象
- 用于代码块时,显式指定锁对象
- 锁的作用域:对象锁、类锁、分布式锁
public Counter{
/** synchronizde(this) ,锁对象,再有多个对象时,锁会失去互斥性
如Count c1 = new Count();
Count c2 = new Count();
...
()-> new Thread({c1.update()});
()-> new Thread({c2.update()});
**/
public synchronized void update(){
i++;
}
public void updateBlock(){
synchronized(this){
i++;
}
}
/** synchronized(Counter.class) ,锁类对象**/
public static synchronized void staticUpdate(){
i++;
}
public static void staticUpdateBlock(){
synchronized(Counter.class){
i++;
}
}
}
synchronized特性:
可重入
独享
悲观锁
锁优化:
锁消除:在单线程中,对同一个对象不停的进行加锁和解锁操作,JIT即时编译时,进行了锁消除.(出现在循环体或者同一方法多次被调用,代码块如下)
开启锁消除的参数:(-XX:+DoEscapeAnalysis -XX:+EliminateLocks)
//当该方法被多次调用时,jit优化消除锁
public void test(){
StringBuffer sb = new StringBuffer();
sb.append("1");
sb.append("2");
sb.append("3");
...
}
锁粗化: 运行时jit编译优化
在编码的时候可以注意锁粗化,频繁加解锁会消耗性能
synchronized(this){
i++;
}
sychronized(this){
i--;
}
synchronized(this){
...
}
synchronize底层原理
synchronized(this){
i++;
}
扩展: 堆内存中的JAVA对象
扩展结束
Mark Word信息:
Monior:对象监视器,其结构如下:
Owner:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL;
EntryQ:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程。
RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。
Nest:用来实现重入锁的计数。
HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。
Candidate:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻塞)从而导致性能严重下降。Candidate只有两种可能的值0表示没有需要唤醒的线程1表示要唤醒一个继任线程来竞争锁。