在真实场景中锁,锁并没有那么多,很多锁只是从不同功能特性,设计,以及锁的状态这些不同的侧重点来说明的,因此我们根据不同的分类来理解。
1.公平锁和非公平锁
公平锁
指线程在等待获取同一个锁的时候,是严格按照申请锁的时间顺序来进行的,这就意味着在程序运作的时候,不会有线程执行不到的情况,但需要额外的资源维护这种顺序所以效率相对于非公平锁会差点。
非公平锁
随机线程获取锁,效率比公平锁相对高些。
2.重入锁(递归锁)与不可重入锁(自旋锁)
重入和递归,不可重入和自旋虽然名字不同,但是确实是同一种锁,只是从锁的表现跟实现方式的角度来命名而。
重入锁(递归锁)
当一个线程获取A锁以后,若后续方法运行被A锁锁住的话,当前线程也是可以直接进入的。
public class Demo {
public Lock lockA;
public Demo(Lock lock) {
lockA = lock;
}
public void methodA() {
lockA.lock();
methodB();
System.out.println("methodA");
lockA.unlock();
}
public void methodB() {
lockA.lock();
System.out.println("methodB");
//dosm
lockA.unlock();
}
}
当我们运行methodA()的时候,线程获取了lockA,然后调用methodB()的时候发现也需要lockA,由于这是一个可重入锁,所以当前线程也是可以直接进入的。在java中,synchronized跟ReetrantLock都是可重入锁。
不可重入锁(自旋锁)
是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。在Java中,那就是CAS。
CAS机制中使用了三个基本操作数,内存地址V,旧的预期值A,要修改的新值B。更新共享变量的时候,只有当变量的预期值A和内存地址V当中的实际值是相同的,才会将内存地址V对应的值修改为B。
3.悲观锁与乐观锁
这两种锁呢,不是一种具体的锁,而是泛指看待并发的程度。
悲观锁
有一个悲观的心态,即每次取数据的时候,都会认为该数据会被修改,所以必须加一把锁才安心。
有:ReentrantLack ,synchronized
乐观锁
乐观锁认为同一个数据不会发生并发操作的行为,所以取的时候不会加锁,只有在更新的时候,会通过例如版本号之类来判断数据是否被修改。
CAS,AtomicInteger
4.共享锁与拍他锁
这两种锁的概念比较多的出现在数据库的事物中。
共享锁
也称为读锁或者S锁,如果事物对数据A加上共享锁后,其他事物只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。在Java中的ReetrantReaderWriteLock()也是如此。
排他锁
也称独占锁、写锁或X锁。如果事务对数据A加上排他锁后,则其他事务不能再对A加任何类型的锁。获得排他锁的事物既能读数据又能修改数据。
5.分布式锁
我们上面聊的这些锁,都是在单个程序上面不同线程之间来实现的,那么我们不同程序需要去竞争同一块资源的时候,就需要分布式锁了。我们可以通过redis、zookeeper等中间件来实现分布式锁。