声明:如果本文有错误,希望指出。
数据库中的各种锁可以前往数据库锁机制查看。Java中提供的各种锁可以实现并发编程。
锁是用来控制多个线程访问共享资源的方式,一辩来说,一个锁能够防止多个线程同时访问共享资源。
可重入锁
可重入锁 ,也叫做递归锁,指的是同一线程外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。
在JAVA环境下 ReentrantLock 和 Synchronized 都是 可重入锁。
可重入锁最大的作用是避免死锁。
自旋锁
自旋锁是采用让当前线程不停地在循环体内执行,当循环的条件被其他线程改变时才能进入临界区。
自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不断增加时,性能下降明显,因为每个线程都需要执行,会占用CPU时间片。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。
独享锁
独享锁是指该锁一次只能被一个线程所持有。ReentrantLock 、Synchronized 都是独享锁。
1 | Lock lock = new ReentrantLock(); |
ReetrankLock 与 synchronized 比较
synchronized 在1.5版本之前性能低下,因为这是一个重量级操作,它对性能最大的影响是阻塞的是实现,挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给系统的并发性带来了很大的压力。但在之后的JDK版本中进行了大量的优化,性能已经和ReetrankLock相媲美。 使用 synchronized 关键字将会隐式的获取锁,但是它将锁的获取和释放固定化了,也就是先获取再释放。
Synchronized 底层实现原理:
- Synchronized 同步语句块的实现使用的是
monitorenter
和monitorexit
指令,其中monitorenter
指令指向同步代码块的开始位置,monitorexit
指令则指明同步代码块的结束位置。 - Synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的确实是
ACC_SYNCHRONIZED
标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。
ReentrantLock与synchronized很相似,它们都具备一样的线程重入特性,只是代码写法上有点区别而已,一个表现为API层面的互斥锁(Lock),一个表现为原生语法层面的互斥锁(Synchronized)
共享锁
共享锁是指该锁可被多个线程所持有。ReentrantReadWriteLock,其读锁是共享锁,其写锁是独享锁。读锁的共享锁可保证并发读是非常高效的,读写、写读、写写的过程是互斥的。独享锁与共享锁也是通过AQS(AbstractQueuedSynchronizer)来实现的,通过实现不同的方法,来实现独享或者共享
互斥锁
独享锁/共享锁就是一种广义的说法,互斥锁/读写锁指具体的实现。互斥锁在Java中的具体实现就是ReentrantLock
读写锁
在Java中读写锁的具体实现是 ReentrantReadWriteLock
,这个类在java.util.concurrent.locks
包下面。
1 | private static volatile ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(); |
下面这篇博客对读写锁分析的挺不错的。
公平锁和不公平锁
两者的区别主要体现在是否按照申请锁的顺序来获取锁。
- 公平锁:多个线程按照申请锁的顺序来获取锁。
- 不公平锁:非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。可能造成优先级反转或者饥饿现象。对于Java ReentrantLock而言,通过构造函数 ReentrantLock(boolean fair) 指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。对于Synchronized而言,也是一种非公平锁。