乐观锁

乐观锁是一种乐观思想,认为读多写少,遇到并发写的可能性低,每次去那数据的时候都认为别人不会修改,所以不会上锁。但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复 读-比较-写 的操作。 比如:Java中的CAS操作;数据库中的共享锁。

悲观锁

是一种悲观思想,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样被人想读这个数据就会阻塞,直到拿到锁。例如:Java中的synchronized;数据库中的排他锁;Java中AQS框架下的锁则是先尝试CAS乐观锁去获取锁,获取不到才会转为悲观锁,比如ReentrantLock。

公平锁

线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。

非公平锁

在运行时闯入,先来的不一定先得。

独占锁

保证任何时候都只有一个线程能得到锁。是一种悲观锁,由于每次访问资源都先加上互斥锁,这限制了并发性,因为读操作并不会影响数据的一致性,而独占锁只允许在同一时间由一个线程读取锁具,其他线程必须等待当前线程释放锁才能进行读取。
(貌似独占锁就是互拆锁,只是名字不同。也叫排它锁、写锁、X锁。 而共享锁,又叫读锁,S锁)

互斥锁和共享锁

互斥锁是指该锁一次只能被一个线程所持有。共享锁是指该锁可被多个线程持有,放宽了加锁的条件,允许多个线程同时进行读操作。
ReentrantLock、synchronized是互斥锁;
ReentrantReadWriteLock,其读锁时共享锁,写锁时互斥锁。
互斥锁和共享锁也是通过AQS来实现的,通过实现不同的方法实现共享或者互斥。

可重入锁和不可重入锁

又叫递归锁。一个线程对于已经获取到了的锁,可以不被阻塞,直接获取锁。或者这么理解:指同一个线程在外层方法获取锁的时候,在进入内层方法时会自动获取锁。可重入锁的一个好处就是可以一定程度上避免死锁。(synchronized也是一个可重入锁)
例:

  1. public sychrnozied void test() {
  2. xxxxxx;
  3. test2();
  4. }
  5. public sychronized void test2() {
  6. yyyyy;
  7. }

这上面代码中,执行test()方法需要获得当前对象作为锁,但是test()方法中又调用了test2()这个同步方法;

  • 如果锁时具有可重入性的话,那么该线程在调用test2()时不需要再次获得当前对象的锁,可以直接进入test2()方法进行操作;
  • 如果锁不具有可重入性,那么该线程在调用test2()前会等待当前对象锁的释放,实际上该对象已被当前线程所持有,不可能在此获得。

如果锁时不具有可重入性特点的话,那么线程在调用同步方法、含有锁的方法时,就会产生死锁。

可重入锁:
又递归锁,是指在同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提:锁对象得是同一个对象),不会因为之前已经获取过锁还没释放而阻塞。
ReentrantLock和synchronized都是可重入锁,可重入锁可以在一定程度上避免死锁。
也可这么理解:一个线程中多个流程可以获取同一把锁,持有这把同步锁可以再次进入;自己可以获取自己的内部锁。

自旋锁

当前线程在获取锁时,如果发现锁已经被其他线程占有,它不马上阻塞自己,在不放弃CPU使用权的情况下,多次尝试获取(默认次数是10,可以使用-XX:PreBlockSpinsh参数设置该值),很有可能在后面几次尝试中其他线程已经释放了锁。如果尝试指定次数后仍没有获取到锁,则当前线程才会被阻塞挂起。自旋是通过CAS算法进行的。