重入锁
如果某个线程试图获取一个已经由他自己持有的锁,这个请求可以成功,那么此时的锁就是可重入锁,重入锁的这种机制也说明了它是以”线程“为粒度获取锁,而不是以”调用“为粒度。重入常见的一个实现方法是,为每个锁关联一个持有者和持有计数值,当计数值为0时,这个锁会被认为没有被任何线程持有,当现场请求一个未被持有的锁时,jvm会把这个锁给这个线程,并几下这个锁的持有者,同时计数值置为1,如果同一个线程再次获取这个锁,计数值将递增,当线程退出同步代码块时,计数值将递减,当计数值为0时,锁将被释放。
常见的重入锁有synchronized和ReentrantLock(Reentrant本身就是重入的意思)
对于重入的理解见下面的例子(以synchronized为例):
首先定义一个父类SuperSyncObj,它的方法用synchronized修饰
public class SuperSyncObj {
public synchronized void methodA(){
System.out.println("super class running methodA");
}
}
然后SyncObj继承这个父类,重写methodA,当然methodA也用synchronized修饰
public class SyncObj extends SuperSyncObj{
@Override
public synchronized void methodA() {
System.out.println("methodA.....");
try {
super.methodA();
methodB();
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
最后运行结果:
methodA.....
super class running methodA
methodB.....
可以看到,如果synchronized不具有重入性,执行super.methodA()的时候会出现死锁,但是synchronized具有重入性,执行到这一步的时候,锁会检测当前持有锁的线程,发现正是请求这个锁的线程本身,于是请求可以成功,同理methodA调用methodB, 因为也是同一个线程请求同一把锁,也可以成功
不可重入锁
不可重入锁和可重入锁相反,就是同一个线程多次请求同一把锁,会出现死锁
(1)现在模拟一个不可重入自旋锁。
public class UnreentrantLock {
private AtomicReference<Thread> owner = new AtomicReference<Thread>();
public void lock(){
Thread current = Thread.currentThread();
while(!owner.compareAndSet(null, current)){}
}
public void unlock(){
Thread current = Thread.currentThread();
owner.compareAndSet(current, null);
}
}
1、若有同一线程两调用lock() ,会导致第二次调用lock位置进行自旋,产生了死锁
说明这个锁并不是可重入的。(在lock函数内,应验证线程是否为已经获得锁的线程)
2、若1问题已经解决,当unlock()第一次调用时,就已经将锁释放了。实际上不应释放锁。
我们使用UnreentrantLock:
public class UnreentrentLockObj {
UnreentrantLock lock = new UnreentrantLock();
public void lockFunc(){
lock.lock();
System.out.println("lockFunc...");
lockFunc2();
lock.unlock();
}
public void lockFunc2(){
lock.lock();
System.out.println("lockFunc2...");
lock.unlock();
}
public static void main(String[] args) {
final UnreentrentLockObj obj = new UnreentrentLockObj();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
obj.lockFunc();
}
});
thread.start();
}
}
这时候会发现,在调用lockFunc2()的时候出现了死锁
(2)改进一下,改为可重入锁
public class UnreentrantLock {
private AtomicReference<Thread> owner = new AtomicReference<Thread>();
private int count = 0;
public void lock(){
Thread current = Thread.currentThread();
if(current == owner.get()){
count++;
return;
}
while(!owner.compareAndSet(null, current)){}
}
public void unlock(){
Thread current = Thread.currentThread();
if(count > 0){
count--;
}else{
owner.compareAndSet(current, null);
}
}
}
这样上面的调用就不会发生死锁