重入锁:Lock有一个实现类 ReentrantLock (又名可重入锁),这种锁是可以反复多次进入的,其局限性在于同一个线程内
public class Lock{
private boolean isLocked = false;
public synchronized void lock() throws InterruptedException{
while(isLocked){
wait(); //等待
}
isLocked = true;//设为true意思是等待中被锁住
}
public synchronized void unlock(){
isLocked = false; //意思是解锁
notify();//唤醒
}
}
不可重入锁:又叫自旋锁,只能调用一个锁方法,示例:
public class Count{
Lock lock = new Lock();
public void print(){
lock.lock();
doAdd();
lock.unlock();
}
public void doAdd(){
lock.lock();
//do something
lock.unlock();
}
}
当调用print()方法时,获得了锁,这时就无法再调用doAdd()方法,这时必须先释放锁才能调用,所以称这种锁为不可重入锁,也叫自旋锁
可重入锁:线程可以进入任何一个它已经拥有的锁所同步着的代码块
public class Lock{
boolean isLocked = false;
Thread lockedBy = null;
int lockedCount = 0;
public synchronized void lock()
throws InterruptedException{
Thread thread = Thread.currentThread();
while(isLocked && lockedBy != thread){
wait();
}
isLocked = true;
lockedCount++;
lockedBy = thread;
}
public synchronized void unlock(){
if(Thread.currentThread() == this.lockedBy){
lockedCount--;
if(lockedCount == 0){
isLocked = false;
notify();
}
}
}
}
第一个线程执行print()方法,得到线程锁A,使lockedBy等于线程锁A,执行add()方法时,同样要先获取线程锁A,因不满足while循环的(isLocked = false)条件,也就是不等待,继续进行,将此时的lockedCount变量+1,当释放了所有的锁,才执行notify()。如果在执行这个方法时,有第二个线程B想要执行这个方法,因为lockedBy不等于第二个线程,isLocked 为true,导致这个线程B进入了循环,不断执行wait()方法。只有当第一个线程释放了所有的锁,执行了notify()方法,第二个线程B才得以跳出循环,继续执行。
Synchronized、java.util.concurrent.locks.ReentrantLock:都是可重入锁
ReadWriteLock:读写锁,读写锁在读的时候,上读锁,在写的时候,上写锁,这样就很巧妙的解决synchronized的一个性能问题:读与读之间互斥。分成2个锁来分配给线程,从而可以做到读和读互不影响,读和写互斥,写和写互斥,提高读写文件的效率
可中断锁:如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。在Java中,synchronized就不是可中断锁,而Lock是可中断锁。
公平锁:公平锁即尽量以请求锁的顺序来获取锁。比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。
非公平锁即无法保证锁的获取是按照请求锁的顺序进行的。这样就可能导致某个或者一些线程永远获取不到锁。
在Java中,synchronized就是非公平锁,它无法保证等待的线程获取锁的顺序。
而对于ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。设置方法如下:ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
非公平锁: