一、显示锁
1、synchronized和lock辨析
Java中的synchronized是一种内置锁
,其中加锁解锁的顺序已经被固化,并且没有提供一些其他可供选择的操作,java还提供了一种显示锁lock,它提供了一系列的方法供我们使用:可以尝试获取锁,超时获取锁、获取锁可以被中断,如果使用锁时有以上3中业务需求,则需要使用lock,否则使用synchronized即可。
2、ReentrantLock可重入锁
java中的lock是一个接口,Java ReentrantLock实现了lock,是可以被实例化的类,同时它也支持可重入
的机制。
- 概念:在同一个线程中获取锁之后再次获取锁,可以获取成功的就是可重入锁
- 场景:可重入锁的使用场景是哪些,一个比较典型的就是递归,递归调用很明显如果方法中存在加锁的操作,那么就会存在一个线程连续对同一把锁进行加锁
- 举例实现不可重入锁、可重入锁
2.1不可重入锁
package cn.enjoy.controller.thread;
/**
* @author:wangle
* @description:
* @version:V1.0
* @date:2020-03-28 14:22
**/
public class Lock {
private boolean isLock = false;
//因为需要使用到wait和notify在这里使用了synchronized
public synchronized void lock()throws InterruptedException{
while(isLock){
wait();
}
isLock=true;
}
public synchronized void unLock(){
isLock=false;
notify();
}
public class Count{
Lock lock = new Lock();
public void out()throws InterruptedException{
lock.lock();
doLock();
lock.unLock();
}
public void doLock()throws InterruptedException{
lock.lock();
Thread.sleep(1000);
lock.unLock();
}
}
}
上述程序很明显第二次被加锁的时候就会被阻塞
2.2可重入锁
package cn.enjoy.controller.thread;
/**
* @author:wangle
* @description:
* @version:V1.0
* @date:2020-03-28 14:22
**/
public class Lock {
private boolean isLock = false;
Thread currentThread = null;
int lockCount = 0;
//因为需要使用到wait和notify在这里使用了synchronized
public synchronized void lock()throws InterruptedException{
Thread thread = Thread.currentThread();
while(isLock && thread != currentThread){
wait();
}
isLock=true;
currentThread=thread;
lockCount+=1;
}
public synchronized void unLock(){
Thread thread = Thread.currentThread();
while(thread == currentThread){
lockCount--;
if(lockCount==0){
isLock=false;
currentThread=null;
notify();
}
}
}
public class Count{
Lock lock = new Lock();
public void out()throws InterruptedException{
lock.lock();
doLock();
lock.unLock();
}
public void doLock()throws InterruptedException{
lock.lock();
Thread.sleep(1000);
lock.unLock();
}
}
}
上述程序就是一个可重入锁,当第一个线程进行加锁的时候由于currentThread==null,不会进入while进行wait,再次进入的时候发现currentThread是等于当前线程的,也不会进入到while循环进行等待,两次都是进行了lockCount的自增,解锁时如果是当前线程获取锁则去lockCount自减,如果减到0则释放锁。
java中的ReentrantLock可重入锁也是基于这种机制进行的。
2.3、公平锁、非公平锁
- 概念
在时间上,先对锁进行获取的请求一定先被满足,就是公平锁,否则就是非公平锁。 - 公平锁和非公平锁的效率问题
举例:
1、线程A获取了锁
2、线程B来请求获取锁,发现锁已经被占有,则线程B被挂起
3、线程C来进行获取锁。
这时A释放了锁
公平锁的操作如下
1、唤醒B线程,执行某些业务操作,再释放锁,这时候C很有可能已经被挂起了,因为长时间未获取到锁,这时还要唤醒C再去执行业务操作
非公平锁的操作如下
1、线程C在尝试获取锁的时候还未被挂起,A释放了锁,则直接获取锁,执行相关操作,再唤醒B线程,获取锁执行操作。
可以看出非公平锁的效率是比较高的,省去了B的唤醒操作的时间,还有可能省去C的唤醒时间。
ReentrantLock提供2个重载的构造函数,带有boolean参数的可以指定是否是公平锁
3、读写锁ReadWriteLock
ReadWriteLock是一个接口,ReentrantReadWriteLock实现了这个接口
Synchronized、ReentrantLock都是排他锁,一个线程持有锁其他线程都得等待其释放后才能重新获取锁执行。
读写锁:同一时刻允许多个线程同时访问,但是写线程访问的时候,其他所有读写线程都将被阻塞,最适宜读多写少的场景。
4、lock、condition
condition接口有比较重要的两个方法,signal
、await。类似于Object的wait和notify方法,功能也是如出一辙。都是用于实现等待于通知机制。用法几乎一毛一样,在这里就不演示了,有兴趣的小伙伴可以去了解一下。