Condtion队列

Condition实现了Object中的监视器方法(wait和notify)。 举个列子:假定我们有一个缓冲区提供了put和get方法。如果一个线程尝试在一个空缓冲区中使用take方法,这个线程就会被阻塞,直到有一个可以获取的元素;如果一个线程尝试在一个满的缓冲区中使用put方法,它就会被阻塞,直到这个缓冲区有空位。我们因该保持执行put和set方法的线程在不同的等待池中,这样我们的操作才会最佳化。

使用:

class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull  = lock.newCondition(); 
final Condition notEmpty = lock.newCondition(); 
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
  lock.lock();
  try {
    while (count == items.length)
      notFull.await();
    items[putptr] = x;
    if (++putptr == items.length) putptr = 0;
    ++count;
    notEmpty.signal();
  } finally {
    lock.unlock();
  }
}
public Object take() throws InterruptedException {
  lock.lock();
  try {
    while (count == 0)
      notEmpty.await();
    Object x = items[takeptr];
    if (++takeptr == items.length) takeptr = 0;
    --count;
    notFull.signal();
    return x;
  } finally {
    lock.unlock();
  }
 }
}

上述程序执行流程:
首先创建了一个ReentrantLock的对象Lock,然后又通过Lock创建了两个condition对象notFull和notEmpty(两个condition队列),也就是说这两个condition对象所属同一个Lock之下(即公用一个等待队列),然后还有一个数组代表着资源。
1、假设有两组线程,取和放要同时修改数组,取和取、放和放、取和放之间都会有竞争
2、线程获取资源(尝试获锁)失败,被放到共用的等待队列之中
3、获取资源成功,会进行一个判断:
a、若取线程获取资源成功,则判断数组是否为空,为空则将这个线程放到notEmpty(condition)队列之中并阻塞它,然后释放锁;不为空则消费资源,将一个在notFull队列中等待时间最长的放线程取出,插入到等待队列中,然后释放锁,并唤醒一个在等待队列中等待时间最长的线程
b、若放线程获取资源成功,则判断数组是否已满,满了则将这个线程放到notFull队列之中并阻塞它,然后释放锁;不为空则消费资源,将一个在notEmpty队列中等待时间最长的取线程取出,插入到等待队列中,然后释放锁,并唤醒一个在等待队列中等待时间最长的线程
4、两组线程相互竞争,重复2、3步骤

Condition接口的方法:

public interface Condition {
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}

AQS中有一个Condition接口的实现类ConditionObject,它作为AQS的内部类可以帮我们实现Condition的大部分方法,但要真正的完成它的功能还得和AQS的一些实现类来共同配合
AQS有这样一些属性与Condition相关联:

  static final class Node {
    static final Node SHARED = new Node();
    static final Node EXCLUSIVE = null;
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;
    volatile int waitStatus;
    volatile Thread thread;
    Node nextWaiter;
    Node() {
    }
    Node(Thread thread, int waitStatus) {
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

Node节点,每个线程都会被包装为节点,在Condition队列中SHARED会失效,因为它只支持独享模式;它是一个有三个指针域的节点,但是它的prev和next指针域应该失效,因为Condition队列中的节点使用nextWaiter来代表后继,next是用在等待队列中的指向下一节点的指针

执行流程:

1、构造

final Lock lock = new ReentrantLock();

上面的例子,首先构造了一个ReentrantLock对象

final Condition notFull  = lock.newCondition(); 

然后调用了ReentrantLock的newCondition方法

public Condition newCondition() {
    return sync.newCondition();
}

newCondition方法又会返回AQS中的newCondition方法

final ConditionObject newCondition() {
        return new ConditionObject();
    }

newCondition返回的是一个ConditionObject对象,ConditionObject是AQS中的一个内部类

2、await

  lock.lock();

首先对代码块加上了ReentrantLock锁,这是获得锁的线程就会往下执行,其余线程会被加入等待队列中,至于会不会被阻塞那就要看acquireQueued方法有没有将其阻塞了

notFull.await();

执行了一段代码后调用以上方法将当前线程进行阻塞。

public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        /*将当前节点加进等待队列*/
        Node node = addConditionWaiter();
        /*释放锁*/
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        while (!isOnSyncQueue(node)) {
            LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null)
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
    }

1、若当前线程被中断则抛出new InterruptedException
2、否则将当前节点加进Condition队列然后释放锁,调用await方法后并不用显示调用unlock方法,它会自动给我们调用release方法去释放锁
3、若当前节点处于Condition队列中则调用底层park方法去阻塞它直到被唤醒或中断
4、若节点不在Condition队列中则它一定在等待队列中,调用acquireQueued方法在等待队列中将其阻塞
5、清理调被取消的节点
6、若在阻塞时被中断则报异常

以下是相关调用代码:

private Node addConditionWaiter() {
        Node t = lastWaiter;
        /*若队列不为空并且队列中对后一个节点被取消掉了,做一次清理*/
        if (t != null && t.waitStatus != Node.CONDITION) {
            unlinkCancelledWaiters();
            t = lastWaiter;
        }
        /*将当前线程包装成节点,并标记成condition状态*/
        Node node = new Node(Thread.currentThread(), Node.CONDITION);
       /*尾插并返回当前节点*/
        if (t == null)
            firstWaiter = node;
        else
            t.nextWaiter = node;
        lastWaiter = node;
        return node;
    }

将新节点加入到等待队列中并返回新的节点
1、若队列不为空并且队列中对最一个节点被取消掉了,做一次清理
2、将当前线程包装成节点,并标记成condition状态
3、尾插并返回当前节点

private void unlinkCancelledWaiters() 

这个方法简单来说就是重构队列,舍弃取消掉的节点

final int fullyRelease(Node node) {
    boolean failed = true;
    try {
    /*获取state值*/
        int savedState = getState();
    /*调用AQS中的release方法,然后release会调用ReentrantLock中重写的tryRelease方法尝试释放锁*/
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}

1、获取state值
2、调用AQS中的release方法,然后release会调用ReentrantLock中重写的tryRelease方法尝试释放锁(在这里嵌套的重入锁将会被完全释放)
3、若成功,返回savedState,失败则抛出异常

final boolean isOnSyncQueue(Node node) {
    if (node.waitStatus == Node.CONDITION || node.prev == null)
        return false;
    if (node.next != null)
        return true;
    return findNodeFromTail(node);
}

判断一个节点,一般是一个初始化在condition队列中的节点,现在是否正在尝试重新获取进入sync(等待)队列中的资格, 若是返回true
1、当前节点在之前addConditionWaiter方法中即被加入Condition队列时已经被标记为CONDITION状态,此方法中再次判断它是否为CONDITION状态,或当它是队列中头节点时,返回false,即证明它不再等待队列中
2、若next域有值,则证明它肯定在等待队列中
3、否则返回findNodeFromTail方法的结果

3、notify

notEmpty.signal();

上述代码用来唤醒线程

public final void signal() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }

相比之下signal方法简单了很多,虽然它会是用来唤醒线程的,但它并不会真正的调用unpark方法去唤醒被阻塞的线程,它只是帮我们做一次标记
这个方法会将等待时间最长的节点从Condition队列中移到等待队列中
1、若当前线程不处于独享模式,抛出异常
2、若Condition队列中有节点则唤醒第一个

以下是相关调用代码:

private void doSignal(Node first) {
        do {
            if ( (firstWaiter = first.nextWaiter) == null)
                lastWaiter = null;
            first.nextWaiter = null;
        } while (!transferForSignal(first) &&
                 (first = firstWaiter) != null);
    }

1、若Condition队列为空,退出循环
2、若头节点被成功放入等待队列则退出循环
3、否则重复1和2

final boolean transferForSignal(Node node) {
/*将节点的waitStatus变为0*/
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;
 /*返回当前节点在等待队列中的前任*/
    Node p = enq(node);
    int ws = p.waitStatus;
 /*前任被阻塞或标记位标记失败*/
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

用来改变节点的waitStatus
1、 若一个节点的state状态改变失败则证明其被取消了返回false
2、waitStatus改变成功,就将当前节点尾插到等待队列中
3、若前任被取消或者将前任的waitStatus标记位-1(即标记当前节点应被阻塞)失败则唤醒前任
所以上面说这里并不会唤醒Condition队列中的节点,只是将其插入到等待队列,然后将其waitStatus从-2变为0。真正的唤醒操作还是得
由unlock方法去调用

相类似的方法:

private void doSignalAll(Node first) {
        lastWaiter = firstWaiter = null;
        do {
            Node next = first.nextWaiter;
            first.nextWaiter = null;
            transferForSignal(first);
            first = next;
        } while (first != null);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
优化代码,GPU加速 def temp_condtion(df, temp_upper, temp_low): return ((df['max_temp']<=temp_upper) & (df['min_temp']>=temp_low)) def soc_condtion(df, soc_upper, soc_low): return ((df['bat_module_soc_00']<=temp_upper) & (df['bat_module_soc_00']>=temp_low)) def current_condtion(df, min_curr, batt_state): if batt_state=='charge': return (df['bat_module_current_00'].abs()>=min_curr) & (df['bat_module_current_00']>=0) elif batt_state=="discharge": return (df['bat_module_current_00'].abs()>=min_curr) & (df['bat_module_current_00']<=0 # 板端运行逻辑 data = {'realtime':[], 'cell_volt':[], 'total_current':[]} index = [] # (total_current[j]<=0) for i in tqdm(df.index[temp_condtion(df, temp_upper, temp_low) & soc_condtion(df, soc_upper, soc_low) & current_condtion(df, min_curr, 'discharge')]: n = 0 k = i while (n <= data_point) & (i <= len(df)-100): idx_list = [] idx_list.append(i) for j in np.arange(i+1, len(df)): if ((sendtime.iloc[j]-sendtime.iloc[k]).total_seconds()>=time_interval): break elif (df['max_temp'].iloc[j]<=temp_upper) & (df['min_temp'].iloc[j]>=temp_low) & \ (df['bat_module_soc_00'].iloc[j]>=soc_low) & (df['bat_module_soc_00'].iloc[j]<=soc_upper) & \ ((sendtime[j]-sendtime[i]).total_seconds()>=sample_interval) & \ ((sendtime.iloc[j]-sendtime.iloc[k]).total_seconds()<=time_interval) & \ (np.abs(total_current[j]-total_current[i])>=curr_interval) & (np.abs(soc[j]-soc[i])<=soc_interval) & \ (np.abs(total_current[j])>=min_curr): n+=1 idx_list.append(j) i = j if ((sendtime.iloc[j]-sendtime.iloc[k]).total_seconds()>=time_interval): break if len(idx_list) >= data_point: print(idx_list) index.append(idx_list)
06-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值