文章目录
- juc
- 一、为什么会有juc
- 二、juc--三大接口
- 1.lock
- 2.condition
- 3.ReadWriteLock
- 二、juc--的默认实现类
- 1.ReentrantLock--lock的默认实现类
- 公平锁,非公平锁
- 2. ReentrantReadWriteLock读写锁--ReadWriteLock的默认实现类
- 读写锁和排它锁
- 总结
juc
juc: java.util.concurrent
juc可太牛掰了,几乎完成里面所有的逻辑代码,都是老爷子一个人完成的;
一、为什么会有juc
因为synchronized在很多场景中是无法完美契合的,为了弥补这个缺陷,juc应运而生;说一个最简单的点:synchronized无法知道我是否获取锁成功,无法主动释放锁,当临界区的代码有问题的时候,就会一直阻塞!
synchronized就那么一无是处嘛? no , 例如: synchronized是肯定不会发生死锁的,但是juc中的可重入锁,当时用不当(加锁与解锁没有成对出现)的时候,就会发生死锁!
二、juc–三大接口
1.lock
源码中的示例代码如上:
- 加锁 lock.lock();
- 释放锁 lock.unlock(); 一般在finally 中; 目的是可以最终释放锁,不会导致发生意外后,一直阻塞
- lock 一般不会单独使用
2.condition
Condition源码示例代码如上:
- 与lock 配合使用
- 当使用lock 加锁解锁后, 可以用Condition来作为线程之间的通信
- Condition中的线程通信方法 和Object的wait/notify基本相似。其中,Condition的await方法对应的是Object的wait方法,而Condition的signal/signalAll方法则对应Object的notify/notifyAll()。但Condition类似于Object的等待/通知机制的加强版。
3.ReadWriteLock
这个可就牛掰了~~,读写锁;
可以获取到读锁和写锁; 适用于读多写少的情况;
- 读锁,允许多个线程同时访问
- 写锁,可以理解为是synchronized, 临界区的代码同一时间只能有一个线程访问;
二、juc–的默认实现类
1.ReentrantLock–lock的默认实现类
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
这个锁默认是非公平的锁,且初始化之后无法改变,只能通过 isFair() 获取当前锁的属性;
公平锁,非公平锁
- NonfairSync和FairSync,分别是非公平锁和公平锁;
- 这里的“公平”,其实通俗意义来说就是“先来后到”,也就是FIFO。如果对一个锁来说,先对锁获取请求的线程一定会先被满足,后对锁获取请求的线程后被满足,那这个锁就是公平的。反之,那就是不公平的。
一般情况下,非公平锁能提升一定的效率。但是非公平锁可能会发生线程饥饿(有一些线程长时间得不到锁)的情况。所以要根据实际的需求来选择非公平锁和公平锁。
ReentrantLock支持非公平锁和公平锁两种。
2. ReentrantReadWriteLock读写锁–ReadWriteLock的默认实现类
它与ReentrantLock的功能类似,同样是可重入的,支持非公平锁和公平锁。不同的是,它还支持”读写锁“。
有一个弊端: 使用读写锁,在写线程访问时,所有的读线程和其它写线程均被阻塞。
读写锁和排它锁
我们前面讲到的synchronized用的锁和ReentrantLock,其实都是“排它锁”。也就是说,这些锁在同一时刻只允许一个线程进行访问。通常说法的锁其实都是排他锁;
而读写锁可以在同一时刻允许多个读线程访问。Java提供了ReentrantReadWriteLock类作为读写锁的默认实现,内部维护了两个锁:一个读锁,一个写锁。通过分离读锁和写锁,使得在“读多写少”的环境下,大大地提高了性能。
总结
juc几乎提供了所有多线程下的,线程通信机制,完美解决了线程中各种场景的问题;