java锁初步认识
锁在java中使用最多的是在对象上面,java对象的简单对象指针Oop的markWord中记录了锁标志,
01:无锁,偏向锁
00:轻量锁(cas)
10:重量锁,阻塞
11:Gc标志
偏向锁:
jdk开发者发现大多时候,一个线程将占有使用一个持有锁对象很久,很少有竞争者,这种情况并不需要锁。所以就记录持有锁的线程id在对象中,下次访问资源的如果还是该线程则放行
轻量锁:
对象处于偏向锁状态,如果再次访问资源的线程并不是记录的线程id,则升级为轻量锁,轻量锁使用cas(比较替换)方法。具体为目标值value 预估值A,修改值B,如果value=A,则B赋值给value ,如果不等于,就一直进行比较,所以这也叫自旋锁,自旋锁默认在jdk1.7后打开,当有其他线程再假如,或者自旋到一定次数,轻量锁会升级为重量锁
每一个线程都会有一个记录锁的结构,LockRecord,轻量锁进行的cas,是LockRecord里的地址和Oop对象里的WorkMark做运算,详见:https://baijiahao.baidu.com/s?id=1717781876275288385&wfr=spider&for=pc
重量锁:
Monitor监视器,是所有对象都与之关联,ObjectMonitor有两个队列
_WaitSet
_EntryList
1:重量锁的线程会进入EntryList 集合,当线程获取到对象的monitor后,monitor的owner会替换为当前线程,同时线程的count+1
2:当线程调用wait()后,线程会释放monitor,owner为null,count-1;同时线程进入_WaitSet等待被唤醒
3:如果当前线程执行完毕,也会释放monitor,并复位count,其他线程就可以来获取monitor
4:synchronized是非公平锁,当ower释放monitor后,并不会按照EntryList记录获取锁
monitor记录在什么地方??对象Oop的markword中
如下图
悲观锁:
对数据的修改抱有悲观态度的并发控制方式。我们一般认为数据被并发修改的概率比较大,所以需要在修改之前先加锁。数据库中的行锁,表锁,读锁,写锁,以及 syncronized 实现的锁均为悲观锁
乐观锁是对于数据冲突保持一种乐观态度,操作数据时不会对操作的数据进行加锁,只有到数据提交的时候才通过一种机制来验证数据是否存在冲突。
锁实现 ReentrantLock公平锁
ReentrantLock lock = new ReentrantLock(true); // true:公平锁
lock.lock();
try {
// todo
} finally {
lock.unlock();
}
//创建一个非公平锁,默认是非公平锁
Lock lock = new ReentrantLock();
Lock lock = new ReentrantLock(false);
//创建一个公平锁,构造传参true
Lock lock = new ReentrantLock(true);
读写锁
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
try {
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + " 正在写入");
} finally {
readWriteLock.writeLock().unlock();
}
自旋锁:
public class SpinLock {
private AtomicReference cas = new AtomicReference();
public void lock() {
Thread current = Thread.currentThread();
// 利用CAS
while (!cas.compareAndSet(null, current)) {
// DO nothing
}
}
public void unlock() {
Thread current = Thread.currentThread();
cas.compareAndSet(current, null);
}
}
锁的好文:
https://blog.csdn.net/zhengzhaoyang122/article/details/110847701