Lock接口
首先要说明的就是Lock,通过查看Lock的源码可知,Lock是一个接口:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
- Lock():获取锁
- lockInterruptibly():可中断地获取锁,即该方法会响应中断,在锁的获取中可以中断当前线程。
- tryLock():尝试获取锁,调用该方法会立即返回,如果能够获取则返回true 否则返回true
- tryLock(long time, TimeUnit unit):超时尝试获取锁,当前线程在超时时间内获得了锁返回true,当前线程在超时时间内被中断返回false,超时时间结束返回false。
- unlock():释放锁
- newCondition():后面再说咯 还没看到=。=
个人的理解:Lock就是一个方法的标准规范,后面如果我们需要实现我们自己的写的同步器的话,需要实现该接口,统一调用的方法。还有就是Lock与synchronized之间的不同之处:
1、synchronized是隐形的获取和释放锁,而lock则需要我们手动的获取和释放。
2、Lock可以让等待锁的线程响应中断,线程可以中断去干别的事务,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断
3、Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,在代码执行时出现异常,JVM会自动释放锁定。lock是通过代码实现的,要保证锁定一定会被释放,就必须将 unLock()放到finally{} 中;
4、通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5、Lock可以提高多个线程进行读操作的效率。例如:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。
但是采用synchronized关键字来实现同步的话,就会导致一个问题:
如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。
因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock(子类)就可以办到。
AQS队列同步器
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
private volatile int state;
}
是用来构建锁或者其他同步组件的基础框架,使用一个int成员变量state来表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
同步器的主要使用方式是继承,子类通过继承同步器并实现他的抽象方法来管理同步状态。
同步器中主要有3类方法:
1、模版方法----用于调用我们实现的抽象方法,来实现具体的语义。
2、同步器提供对同步状态进行修改的基础方法。
3、需要实现的抽象方法----关乎于我们所实现的同步器的。
下面根据源码来一个个分析
模版方法
主要含有:
acquire(int arg): 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,将会进入同步队列等待,该方法将会调用重写的tryAcquire(int arg)。
acquireInterruptibly(int arg): 与acquire相同,但是该方法可以响应中断,当前线程未获取到同步状态而进入同步队列,如果当前线程被中断,则该方法会抛出InterruptedException并返回
boolean tryAcquireNanos(int arg,long naps): 在acquireInterruptibly(int arg)的基础上增加了超时限制。
acquireShared(int arg): 共享式的获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式获取的主要区别是在同一时刻可以有多个线程获取到同步状态。
acquireSharedInterruptibly(int arg): 与acquireShared相同,响应中断
boolean tryAcquireSharedNaos(int arg,long nanos): acquireSharedInterruptibly(int arg)的基础上增加了超时限制。
boolean release(int arg): 独占式释放同步状态,该方法会在释放同步状态之后,将同步队列中的一个节点包含的线程唤醒。
boolean releaseShared(int arg): 共享式释放同步状态
Collection getQueuedThreads(): 获取等待在同步队列上的线程集合
举例子:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
public final boolean release(int arg) {
//释放锁成功后
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒头节点后面的线程来进行GGGGGGGG
unparkSuccessor(h);
return true;
}
return false;
}
上面分别的给出了其中的两个模版方法。同步器本身是采用了模版方法的设计模式。
何为模版方法设计模式???
就是通过一个抽象类定义好了模版方法,模版方法中通过组合不同的抽象方法来实现具体语义,而抽象方法的具体实现由用户实现的子类来实现,即模版方法定义好了调用那些抽象方法来实现一个功能,而底层的实现由抽象方法来实现。为了保证模版方法的不可改变 都采用了final进行修饰。
可以看出来acquire方法内部调用了tryAcquire() 这个tryAcquire就是我们需要实现的抽象方法。release方法内部也是调用了tryRelease()---->抽象方法
同步器提供对同步状态进行修改的基础方法
主要含有:
getState(): 获取当前同步状态。
setState(int newState): 设置当前同步状态。
compareAndSetState(int expect,int update): 使用CAS来设置当前状态,该方法能够保证状态设置的原子性。
举例子:
protected final int getState() {
return state;
}
上面提出了其中一个getstate方法,后面我们继承同步器所重写的抽象方法就需要依据这几个同步器提供的方法来实现我们需要实现的语义。
这些虽然由final修饰,但是并不属于模版方法,因为其没有调用需要我们重写的模版方法。so 这顶多算是同步器已经实现好的一些基础方法。
需要实现的抽象方法
主要含有:
boolean tryAcquire(int arg): 独占式获取共享状态,实现该方法需要查询当前状态(getState)并判断同步状态是否符合预期,然后再进行CAS设置同步状态
boolean tryRelease(int arg): 独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态。
int tryAcquireShared(int arg): 共享式获取同步状态,返回大于等于0的值,表示获取成功,反之,获取失败。
boolean tryReleaseShared(int arg): 共享式释放同步状态
Boolean isHeldExclusively(): 当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所占。
//AQS中的抽象tryRelease方法
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
//可重入锁ReentrantLock的内部类Sync实现了AQS实现的tryRelease方法
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//判断当前线程是否拥有锁
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}
}
ReentrantLock可重入锁这个同步组件,通过持有一个内部类,这个内部类继承了AbstractQueuedSynchronizer实现了一个同步器,并且按照自己同步器的需求实现了抽象方法。(sync下还有FairSync or NonfairSync 公平锁以及非公平锁)。
总结:
Lock接口就是一个用于规范调用方法的接口,方便了用户。,AQS同步器就是一个为了方便我们自己实现同步组件的,实现一个同步组件的过程:建立一个同步组件实现lock接口,规范好标准的方法,然后实现一个内部同步器累继承与AQS,实现其抽象方法。提供模版方法给lock接口中的标准方法调用。