同步器和自定义同步组件

来源:互联网 发布:linux u盘 mknod 编辑:程序博客网 时间:2024/06/07 00:05

队列同步器(AbstractQueuedSynchronizer):并发包的作者(Doug Lea)期望用它来实现大部分同步需求,即自定义同步组件,可以实现我们自己所需要的同步策略。

主要有三种模式:

1: void acquire(int arg) 独占式获取同步状态,成功则返回,失败则进入等待队列末尾(调用CAS(compareAndSetTail(Node expect,Node update))进入等待队列尾部避免因为竞争而无序),进入等待队列的同时将进入自旋过程(for循环),每个节点都在观察自己的前驱节点是否是头节点,是则尝试获取同步状态,获取成功则返回,否则继续自旋。获取同步状态时调用的是队列同步器的抽象方法 boolean tryAcquire(int arg),此方法需要我们去实现来满足自己的自定义同步组件的需求。实现此方法时需要使用同步器提供的三个方法来访问和修改同步状态:getState(),setState(int newState),compareAndSetState(int expect,int update).

2:void acquireInterruptibly(int arg)与1相同,同时该方法可以响应中断,当前线程如果在同步队列中可以被中断,会抛出中断异常并返回。

3:void tryAcquireNanos(int arg,long nanos)在2的基础上增加了超时限制,当线程在同步队列中等待了预置时间还没有获取到同步状态,则也会返回。超时返回主要使用了当前唤醒时间-上次唤醒时间来得到已经睡眠时间。用需要睡眠的时间-已经睡眠的时间得到还需睡眠的时间,如果还需睡眠的时间<0,即使不满足获取同步状态的条件,也会立刻返回。

这三种模式每一种都对应有一个共享式获取同步状态的方法,void acquireShared(int arg),void acquireSharedInterruptibly(int arg),boolean tryAcquireSharedNanos(int arg,long nanos)

共享式获取同步状态和独占式获取同步状态主要区别在于:独占式资源同一时刻只能被一个线程访问,而共享式资源同一时刻可以被多个线程访问。

ReentrantLock(重入锁)有俩个特性:

1:线程可以再次获取锁,是通过在nonfairTryAcquire中加入当锁已经被获取时判断是否当前线程为获取锁的线程来实现的,如果是,则可以继续获取锁。

2:支持公平锁和非公平锁,ReentrantLock通过实现自己的自定义同步器Syn extendsAbstractQueuedSynchronizer,实现方法nonfairTryAccquire(int arg),然后通过继承Syn分别实现俩个自定义同步器FairSyn和NonfairSyn,这俩个自定义同步器如上所诉分别实现自己的抽象方法tryAcquire(int arg),对于NonfairSyn则直接调用nonfairTryAccquire(int arg)来实现自己的tryAcquire(int arg),而对于FairSyn则是在获取锁时加入同步队列中是否有前驱节点的判断来保证公平性,如果有前驱节点,则表示有线程应该比当前线程优先获取锁。

ReentrantLock默认是使用非公平锁,是因为非公平锁开销更小。

ReentrantReadWriteLock(读写锁):被用来进行更高并发的访问,允许同一时刻有多个线程进行读取资源。

读写状态的设计使用一个整形变量,其中高16位是读状态,低16位是写状态,可以获取写锁被获取的次数以及获取写锁的线程。

写锁的获取:当不存在读锁和写锁或者当前获取写锁的线程正是当前线程,则可以获取写锁。

写锁的释放:每次释放减少写状态。

读锁的获取:没有写锁被获取或者获取写锁的线程是当前线程,则可以获取读锁(CAS保证线程安全)。

读锁的释放:每次释放减少写状态。

锁降级:是指先获取写锁,再获取读锁,然后释放写锁。为什么使用锁降级:当线程获取写锁更新数据后其他线程必须等到当前线程使用完数据才能获取写锁进行更新。

相比于Object 的等待通知模型,Condition的等待通知模型有以下区别:

1:支持多个等待队列,

2:支持进入等待状态到将来的某个时间,

3:支持不响应中断,

主要有以下方法:

void await() throws InterruptedException:被通知返回表明已经获取锁,被中断则抛出中断异常。

viod awaitUninterruptibly():只能等到被通知返回。

long awaitNanos(long nanosTimeout)throws InterruptedException被通知返回表明已经获取锁(返回剩余的时间),被中断则抛出中断异常,超时返回0或者负数。

long awaitUntil(Date deadline)throws InterruptedException:没有到达指定时间就被通知则返回true,到达指定时间未被通知,则返回false,中断抛出中断异常。

void signal() :唤醒一个在等待队列中的线程。

void signalAll():唤醒所有等待队列上的线程。

Condition等待时(调用await()一系列方法):将同步队列中的首节点(即获取同步状态的节点)移动到等待队列中的尾节点,同时线程状态变为等待状态,阻塞此线程,阻塞的调用使用了LockSupport工具。

Condition通知时(调用signal()和sgnalAll()):将等待队列中的头节点移动到同步队列中,同时推出阻塞状态加入到获取同步状态的竞争中,成功获取锁之后从await()方法返回。

LockSupport有俩类阻塞方式:

没有阻塞对象:

void park():阻塞当前线程,调用unpark()方法或者被中断返回。

void parkNanos(long nanos):在park基础上增加了超时返回。

void parkUntil(long deadline):在park基础上增加到了截止时间返回。

有阻塞对象:在上面的方法上多传递一个参数Object blocker,比如park(Object blocker),使用此类方法,通过线程dump能查看该线程的阻塞对象。


原创粉丝点击