
JUC
Java 锁的使用
- 公平锁/非公平锁
- 可重入锁(递归锁)
- 读锁/写锁
公平锁和非公平锁
是什么公平锁和非公平锁
公平锁
先来后到,像是一个队列
多个线程按照申请锁的顺序来获取锁。
非公平锁
允许加塞
指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程先获取锁。在高并发的情况下,有可能会造成优先级反转或者饥饿现象。
公平锁和非公平锁的区别
并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是boolean为false的非公平锁。
公平锁
Threads acquire a fair lock in the order in which they requested it.
在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程时等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO(FirstInFirstOut)的规则从队列中取到自己。
非公平锁
A nonfair lock premits barging: threads requesting a lock can jump ahead of the queue of waiting threads if the lock happens to be available it is requested.
请求锁时尝试占用锁,如果占用失败,再尝试公平锁的方式。
对于ReentrantLock而言,非公平锁比公平锁吞吐量大。
Synchronized 也是一种非公平锁。
可重入锁
什么是可重入锁
可重入锁(又名递归锁)
指的是同一线程外层函数获得锁之后,内侧递归函数仍然能获得该锁的代码。在同一个线程在外侧方法获取锁的时候,再进入内层方法会自动获取锁。
也就是说,线程可以进入任何一个他已经拥有的锁所同步着的代码块。
1 | public sync void method0x00(){ |
在一个同步方法中访问另一个同步方法,两个方法用的是同一把锁。
ReentrantLock/Synchronized就是一个经典的可重入锁
可重入锁的最大作用
避免死锁
case 1:
1 | /** |
运行结果:
1 | /** |
case2:
1 | /** |
运行结果
1 | /** |
自旋锁(spinlock)
是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗cpu
1 | //unsafe.getAndAddInt() |
SpinLockDemo
手写自旋锁
1 | /** |
独占锁(写锁)/共享锁(读锁)/互斥锁
独占锁
指该锁一次只能被一个线程持有。对
ReentranLock和Synchronized而言都是独占锁共享锁
指该锁可悲多个线程持有
对
ReentrantReadWriteLock其读锁是共享锁,写锁是独占锁读锁的共享锁可保证并发读,是非常高效的。
读写,写读,写写的过程是互斥的。
接下来手写一个读写锁
1 | /** |
执行结果:
1 | /** |
写入的时候不会被打断,读取的时候可以一起读取