我们可以从4个方面来讲述锁的分类。
第一个是:可重入锁和不可重入锁
Java中提供的synchronized、ReentrantLock、ReentrantReadWriteLock都是是可重入锁。
而线程池中的Wroker对象就是不可重入锁,他实现了AQS
可重入锁是说,我一个线程A,他获取了锁B,在执行业务的过程中,再次去获取锁B,这个是被允许的,锁B内部会有一个持有次数,获取一次就+1,那么解锁的时候,也需要解两次,才能释放掉这个锁。
而不可重入锁,就是我获取了锁之后,必须先释放掉这个锁,才能再次获取此锁,不能说我已经拿了锁B,我又再次去拿锁B,这个是不被允许的。
第二个是:乐观锁和悲观锁
在java中,synchronized、ReentrantLock、ReentrantReadWriteLock都是悲观锁
而CAS操作,就是乐观锁
悲观锁是说,我真的上锁,同一时刻,只允许有一个线程获取锁,没获取到的线程,都将阻塞或者挂起,这个过程就涉及到用户态和内核态的一个切换,会消耗大量的时间。
用户态:JVM可以自行执行的命令,不需要借助操作系统
内核态:JVM不可以自行执行,必须要操作系统来执行。
乐观锁就是,我其实没有真正上锁,我只是不断尝试比较并替换值,如果比较失败了,就重新尝试,直到成功,所以他整个过程是没有涉及到用户态和内核态的一个切换的。
第三个是:公平锁和非公平锁
在java中,synchronized是一个非公平锁
而ReentrantLock和ReentrantReadWriteLock则可以实现公平或者非公平,通过参数控制
公平锁是说,假设A线程在使用资源,B线程在排队,那么此时C线程进入,则跟在B线程后面排队,A执行完成到B,B执行完成则到C。
非公平锁是说,假设A线程在使用资源,B线程在排队,那么此时C线程进入,我先去抢这个锁,如果抢到了,我就直接使用,否则才去排队,这里就会存在一个插队操作,假设此时A线程释放了锁,还没来得及唤醒B线程,则C就插队成功了。
第四个是:互斥锁和共享锁
java中的synchronized和ReentrantLock都是是互斥锁
而ReentrantReadWtiteLock有互斥也有共享。
互斥就是说,同一时刻,只能有一个线程获取到锁资源,其他线程需要等待锁的释放。
共享就是说,同一时刻,允许有多个线程同时获取到锁资源,我们拿ReentrantReadWriteLock来说明。我在写的时候,我是需要上互斥锁的,就是说,同一时刻,只能有一个线程来写。但是,读是共享锁,因为我在读的时候,并没有修改任何内容,所以他不会造成一个线程安全问题,所以我可以允许多个线程同时来读,这样就可以提高我们的效率。