在整个并发编程中,或者说管程中,考虑到了两个问题,分别是互斥和同步问题

互斥,即为同一时刻只能有一个线程访问资源

同步,即为线程之间的通信问题

这就用过Lock和Contition来实现管程,并且Lock解决互斥问题,Condition解决同步问题

为什么会在拥有了Synchronized之后,还会出现Lock这个方法吗

其主要原因是因为,Synchronized在申请资源的时候,如果申请不到,就直接进入了阻塞状态,这种状态中,啥都干不了,连释放已经拥有的资源都没办法解决,进入了线程阻塞中

所以为了解决这个问题,Lock被提出了

新增了三个强而有力的功能

1.能够响应中断,在线程中断的时候,可以释放曾经持有的锁

2.支持超时,如果一段时间没有获取到锁,会进行释放锁(返回一个错误)

3.非阻塞的获取到锁,如果尝试获取到锁失败,并不进入阻塞状态,直接返回

关于Lock如何保证的可见性的

如果是synchronized 的话,保证可见是因为Happens-Before

对于Lock,则是利用了Volatile相关的Happens-Before

在Lock中,有一个volatile的变量state,获取锁的时候,会读写State的值

也利用了这个State的值的改变会对之后操作可见

其具体的流程:

1.对于线程T1,对state的操作Before于unlock();

2.对于线程T2,unlock Before lock

3.那么T1对state的操作对T2可见

顺便说下Lock的具体实现 ReentrantLock,翻译为可重入锁

也就是说,在同一线程中,可以重复的获取到通一把锁

同样,还有可重入函数,可重入函数,是指多个线程同时调用该函数,每个线程都能得到正确的结果,同时支持在一个线程内的线程切换,这就说明可重入函数是线程安全的

在可重入锁中,还有着公平锁和非公平锁

使用ReentranLock时候,可以在构造函数中指定,是否是个公平锁

公平锁的概念很简单,如果是公平锁,那么线程在唤醒等待队列中的线程中的时候,就按照谁等待的时间长就唤醒谁

非公平锁则不提供这个保证,可能导致线程的饥饿

对于使用锁,我们规定了三个原则

永远只在更新对象的成员变量的时候加锁

永远在访问可变的成员变量的时候加锁

永远不再调用其他对象的方法的时候加锁

最后一个,是为了避免在调用其他类的方法的时候加锁,如果其他类中也加锁了,导致了死锁问题的出现

然后是关于Condition,基于Lock的条件

Condition实现了管程中的条件变量

Condition支持了多个条件变量,这是一个显著的进步

例如,实现一个阻塞队列,就要求两个条件方法

在一个Lock中

新建两个Condition

图片

分别对应不同的情况来进行await和signal

图片 图片

Lock和Condition实现的管程,通知和等待需要使用的是await() signal() signAll() 这三个方法

其等价于wait(),notify(),notifyAll()

Condition中不能使用wait()这三个方法,使用了必定出现错误

发表评论

邮箱地址不会被公开。 必填项已用*标注