乐观锁
乐观锁是一种乐观思想,认为读多写少,遇到并发写的可能性低,每次去那数据的时候都认为别人不会修改,所以不会上锁。但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复 读-比较-写 的操作。 比如:Java中的CAS操作;数据库中的共享锁。
悲观锁
是一种悲观思想,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样被人想读这个数据就会阻塞,直到拿到锁。例如:Java中的synchronized;数据库中的排他锁;Java中AQS框架下的锁则是先尝试CAS乐观锁去获取锁,获取不到才会转为悲观锁,比如ReentrantLock。
公平锁
线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。
非公平锁
在运行时闯入,先来的不一定先得。
独占锁
保证任何时候都只有一个线程能得到锁。是一种悲观锁,由于每次访问资源都先加上互斥锁,这限制了并发性,因为读操作并不会影响数据的一致性,而独占锁只允许在同一时间由一个线程读取锁具,其他线程必须等待当前线程释放锁才能进行读取。
(貌似独占锁就是互拆锁,只是名字不同。也叫排它锁、写锁、X锁。 而共享锁,又叫读锁,S锁)
互斥锁和共享锁
互斥锁是指该锁一次只能被一个线程所持有。共享锁是指该锁可被多个线程持有,放宽了加锁的条件,允许多个线程同时进行读操作。
ReentrantLock、synchronized是互斥锁;
ReentrantReadWriteLock,其读锁时共享锁,写锁时互斥锁。
互斥锁和共享锁也是通过AQS来实现的,通过实现不同的方法实现共享或者互斥。
可重入锁和不可重入锁
又叫递归锁。一个线程对于已经获取到了的锁,可以不被阻塞,直接获取锁。或者这么理解:指同一个线程在外层方法获取锁的时候,在进入内层方法时会自动获取锁。可重入锁的一个好处就是可以一定程度上避免死锁。(synchronized也是一个可重入锁)
例:
public sychrnozied void test() {
xxxxxx;
test2();
}
public sychronized void test2() {
yyyyy;
}
这上面代码中,执行test()方法需要获得当前对象作为锁,但是test()方法中又调用了test2()这个同步方法;
- 如果锁时具有可重入性的话,那么该线程在调用test2()时不需要再次获得当前对象的锁,可以直接进入test2()方法进行操作;
- 如果锁不具有可重入性,那么该线程在调用test2()前会等待当前对象锁的释放,实际上该对象已被当前线程所持有,不可能在此获得。
如果锁时不具有可重入性特点的话,那么线程在调用同步方法、含有锁的方法时,就会产生死锁。
可重入锁:
又递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提:锁对象得是同一个对象),不会因为之前已经获取过锁还没释放而阻塞。
ReentrantLock和synchronized都是可重入锁,可重入锁可以在一定程度上避免死锁。
也可这么理解:一个线程中多个流程可以获取同一把锁,持有这把同步锁可以再次进入;自己可以获取自己的内部锁。
自旋锁
当前线程在获取锁时,如果发现锁已经被其他线程占有,它不马上阻塞自己,在不放弃CPU使用权的情况下,多次尝试获取(默认次数是10,可以使用-XX:PreBlockSpinsh参数设置该值),很有可能在后面几次尝试中其他线程已经释放了锁。如果尝试指定次数后仍没有获取到锁,则当前线程才会被阻塞挂起。自旋是通过CAS算法进行的。