1.重入锁
重入锁又名递归锁,当同一个线程在外层方法已经获取锁的时候,再进入内层方法时会自动获取该锁
优点: 可以一定程度避免死锁
java中的
ReentrantLock
与Synchronized
都是重入锁
简单认知
public class Reentrant{
public synchronized void outOne(){//锁对象为this
System.out.println("One");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//进入该方法内时已经获取到了锁(this),此时再调用outTwo()方法时会自动获取到锁(this)
outTwo();
}
public synchronized void outTwo(){//锁对象为this
System.out.println("Two");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//测试
@Test
public void test(){
//执行测试,因为synchronized为重入锁,因此不会出现死锁的情况
new Thread(new Runnable() {
@Override
public void run() {
new Reentrant().outOne();
}
}).start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.自旋锁(spinlock)
自旋锁是非阻塞锁,指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁
- 优点: 不会有线程状态切换,因此响应快
- 缺点: 循环会消耗cpu,死锁问题
- 注意:递归程序使用自旋锁时,不能在持有自旋锁时调用自己,也不能在递归调用时试图获得相同的自旋锁
应用场景
- 使用自旋锁(spinlock)时,临界区要尽量短,不要有显式或者隐式的系统调用(如读写文件操作)
- 临界区: 一个访问共用资源的程序片段,当有线程进入临界区时,其他线程或进程必须等待
- 存在大量线程,竞争激烈时,不适合使用自旋锁(占用cpu时间)
自旋锁的实现
public class SpinLock {
private AtomicReference<Thread> sign =new AtomicReference<>();
public void lock(){
Thread current = Thread.currentThread();
while(!sign .compareAndSet(null, current)){
}//当第二个线程调用lock方法时,导致循环一直被执行(自旋),直到第一个线程调用unlock才结束
}
public void unlock (){
Thread current = Thread.currentThread();
sign .compareAndSet(current, null);
}
}