1. 条件队列的意义 Condition将Object监控器方法( wait , notify和notifyAll )分解为不同的对象,从而通过与任意Lock实现结合使用,从而使每个对象具有多个等待集。 Lock替换了synchronized方法和语句的使用,而Condition替换了Object监视器方法的使用。 条件(也称为条件队列或条件变量)为一个线程暂停执行(“等待”)直到另一线程通知某些状态条件现在可能为真提供了一种方法。 由于对该共享状态信息的访问发生在不同的线程中,因此必须对其进行保护,因此某种形式的锁与该条件相关联。 等待条件提供的关键属性是它自动释放关联的锁并挂起当前线程,就像Object.wait一样。 Condition实例从本质上绑定到锁。 要获取特定Lock实例的Condition实例,请使用其newCondition()方法 2. 条件队列原理 2.1 条件队列结构 条件队列是一个单向链表,在该链表中我们使用nextWaiter属性来串联链表。但是,就像在同步队列中不会使用nextWaiter属性来串联链表一样,在条件队列是中,也并不会用到prev, next属性,它们的值都为null。 队列的信息包含以下几个部分: private transient Node firstWaiter;// 队列的头部结点 private transient Node lastWaiter;// 队列的尾部节点 队列中节点的信息包含以下几个部分: 当前节点的线程 thread 当前节点的状态 waitStatus 当前节点的下一个节点指针 nextWaiter 结构图: 在这里插入图片描述 注意: 在条件队列中,我们只需要关注一个值即可那就是CONDITION。它表示线程处于正常的等待状态,而只要waitStatus不是CONDITION,我们就认为线程不再等待了,此时就要从条件队列中出队。 2.2 入队原理 每创建一个Condtion对象就会对应一个Condtion队列,每一个调用了Condtion对象的await方法的线程都会被包装成Node扔进一个条件队列中 3. 条件队列与同步队列 一般情况下,等待锁的同步队列和条件队列条件队列是相互独立的,彼此之间并没有任何关系。但是,当我们调用某个条件队列的signal方法时,会将某个或所有等待在这个条件队列中的线程唤醒,被唤醒的线程和普通线程一样需要去争锁,如果没有抢到,则同样要被加到等待锁的同步队列中去,此时节点就从条件队列中被转移到同步队列中 1. 条件队列转向同步队列图 图片: https://uploader.shimo.im/f/HQ1slBQjxKVpHB0t.png 注意图中标红色的线 但是,这里尤其要注意的是,node是被 一个一个 转移过去的,哪怕我们调用的是 signalAll()方法也是一个一个转移过去的,而不是将整个条件队列接在同步队列的末尾。 同时要注意的是,我们在同步队列中只使用prev、next来串联链表,而不使用nextWaiter;我们在条件队列中只使用nextWaiter来串联链表,而不使用prev、next.事实上,它们就是两个使用了同样的Node数据结构的完全独立的两种链表。 因此,将节点从条件队列中转移到同步队列中时,我们需要断开原来的链接(nextWaiter),建立新的链接(prev, next),这某种程度上也是需要将节点一个一个地转移过去的原因之一。 2. 条件队列与同步队列的区别 同步队列是等待锁的队列,当一个线程被包装成Node加到该队列中时,必然是没有获取到锁;当处于该队列中的节点获取到了锁,它将从该队列中移除(事实上移除操作是将获取到锁的节点设为新的dummy head,并将thread属性置为null)。 条件队列是等待在特定条件下的队列,因为调用await方法时,必然是已经获得了lock锁,所以在进入条件队列前线程必然是已经获取了锁;在被包装成Node扔进条件队列中后,线程将释放锁,然后挂起;当处于该队列中的线程被signal方法唤醒后,由于队列中的节点在之前挂起的时候已经释放了锁,所以必须先去再次的竞争锁,因此,该节点会被添加到同步队列中。因此,条件队列在出队时,线程并不持有锁。 3. 条件队列与同步队列锁关系 条件队列:入队时已经持有了锁 -> 在队列中释放锁 -> 离开队列时没有锁 -> 转移到同步队列 同步队列:入队时没有锁 -> 在队列中争锁 -> 离开队列时获得了锁 4. 实战用法 例如,假设我们有一个有界缓冲区,它支持put和take方法。 如果尝试在空缓冲区上进行take ,则线程将阻塞,直到有可用项为止。 如果尝试在完整的缓冲区上进行put ,则线程将阻塞,直到有可用空间为止。 我们希望继续等待put线程,并在单独的等待集中take线程,以便我们可以使用仅当缓冲区中的项目或空间可用时才通知单个线程的优化。 这可以使用两个Condition实例来实现一个典型的生产者-消费者模型。这里在同一个lock锁上,创建了两个条件队列fullCondition, notFullCondition。当队列已满,没有存储空间时,put方法在notFull条件上等待,直到队列不是满的;当队列空了,没有数据可读时,take方法在notEmpty条件上等待,直到队列不为空,而notEmpty.signal()和notFull.signal()则用来唤醒等待在这个条件上的线程。 public class BoundedQueue { /** * 生产者容器 */ private LinkedList buffer; /** * 容器最大值是多少 */ private int maxSize; /** * 锁 */ private Lock lock; /** * 满了 */ private Condition fullCondition; /** * 不满 */ private Condition notFullCondition; BoundedQueue(int maxSize) { this.maxSize = maxSize; buffer = new LinkedList(); lock = new ReentrantLock(); fullCondition = lock.newCondition(); notFullCondition = lock.newCondition(); } /** * 生产者 * * @param obj * @throws InterruptedException */ public void put(Object obj) throws InterruptedException { //获取锁 lock.lock(); try { while (maxSize == buffer.size()) { System.out.println(Thread.currentThread().getName() + "此时队列满了,添加的线程进入等待状态"); // 队列满了,添加的线程进入等待状态 notFullCondition.await(); } buffer.add(obj); //通知 fullCondition.signal(); } finally { lock.unlock(); } } /** * 消费者 * * @return * @throws InterruptedException */ public Object take() throws InterruptedException { Object obj; lock.lock(); try { while (buffer.size() == 0) { System.out.println(Thread.currentThread().getName() + "此时队列空了线程进入等待状态"); // 队列空了线程进入等待状态 fullCondition.await(); } obj = buffer.poll(); //通知 notFullCondition.signal(); } finally { lock.unlock(); } return obj; } public static void main(String[] args) { // 初始化最大能放2个元素的队列 BoundedQueue boundedQueue = new BoundedQueue(2); for (int i = 0; i < 3; i++) { Thread thread = new Thread(() -> { try { boundedQueue.put("元素"); System.out.println(Thread.currentThread().getName() + "生产了元素"); } catch (InterruptedException e) { e.printStackTrace(); } }); thread.setName("线程" + i); thread.start(); } for (int i = 0; i < 3; i++) { Thread thread = new Thread(() -> { try { boundedQueue.take(); System.out.println(Thread.currentThread().getName() + "消费了元素"); } catch (InterruptedException e) { e.printStackTrace(); } }); thread.setName("线程" + i); thread.start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } 输出结果: 图片: https://uploader.shimo.im/f/ZbYdtEOSIvSGoInd.png 5. 源码分析 Condition接口中的方法 1. await() 实现可中断条件等待,其实我们以上案例是利用ReentrantLock来实现的生产者消费者案例,进去看源码发现其实实现该方法的是 AbstractQueuedSynchronizer 中ConditionObject实现的 将节点添加进同步队列中,并要么立即唤醒线程,要么等待前驱节点释放锁后将自己唤醒,无论怎样,被唤醒的线程要从哪里恢复执行呢?调用了await方法的地方 中断模式interruptMode这个变量记录中断事件,该变量有三个值: 0 : 代表整个过程中一直没有中断发生。 THROW_IE : 表示退出await()方法时需要抛出InterruptedException,这种模式对应于中断发生在signal之前 REINTERRUPT : 表示退出await()方法时只需要再自我中断以下,这种模式对应于中断发生在signal之后。 public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); // 添加节点到条件队列中 Node node = addConditionWaiter(); // 释放当前线程所占用的锁,保存当前的锁状态 int savedState = fullyRelease(node); int interruptMode = 0; // 如果当前队列不在同步队列中,说明刚刚被await, 还没有人调用signal方法, // 则直接将当前线程挂起 while (!isOnSyncQueue(node)) { LockSupport.park(this); // 线程挂起的地方 // 线程将在这里被挂起,停止运行 // 能执行到这里说明要么是signal方法被调用了,要么是线程被中断了 // 所以检查下线程被唤醒的原因,如果是因为中断被唤醒,则跳出while循环 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } // 线程将在同步队列中利用进行acquireQueued方法进行“阻塞式”争锁, // 抢到锁就返回,抢不到锁就继续被挂起。因此,当await()方法返回时, // 必然是保证了当前线程已经持有了lock锁 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); } addConditionWaiter() 方法是封装一个节点将该节点放入条件队列中 private Node addConditionWaiter() { Node t = lastWaiter; // If lastWaiter is cancelled, clean out. // 如果尾节点被cancel了,则先遍历整个链表,清除所有被cancel的节点 if (t != null && t.waitStatus != Node.CONDITION) { unlinkCancelledWaiters(); t = lastWaiter; } // 将当前线程包装成Node扔进条件队列 Node node = new Node(Thread.currentThread(), Node.CONDITION); // 如果当前节点为空值那么新创建的node节点就是第一个等待节点 if (t == null) firstWaiter = node; // 如果当前节点不为空值那么新创建的node节点就加入到当前节点的尾部节点的下一个 else t.nextWaiter = node; lastWaiter = node; // 尾部节点指向当前节点 return node; // 返回新加入的节点 } 注意: 节点加入条件队列时waitStatus的值为Node.CONDTION。 如果入队时发现尾节点已经取消等待了,那么我们就不应该接在它的后面,此时需要调用unlinkCancelledWaiters来清除那些已经取消等待的线程(条件队列从头部进行遍历的,同步队列是从尾部开始遍历的) private void unlinkCancelledWaiters() { // 获取队列的头节点 Node t = firstWaiter; Node trail = null; // 当前节点不为空 while (t != null) { // 获取下一个节点 Node next = t.nextWaiter; // 如果当前节点不是条件节点 if (t.waitStatus != Node.CONDITION) { // 在队列中取消当前节点 t.nextWaiter = null; if (trail == null) // 队列的头节点是当前节点的下一个节点 firstWaiter = next; else // trail的 nextWaiter 指向当前节点t的下一个节点 // 因为此时t节点已经被取消了 trail.nextWaiter = next; // 如果t节点的下一个节点为空那么lastWaiter指向trail if (next == null) lastWaiter = trail; } else // 如果是条件节点 trail 指向当前节点 trail = t; // 循环赋值遍历 t = next; } } fullyRelease(node) 方法释放当前线程所占用的锁 final int fullyRelease(Node node) { boolean failed = true; try { int savedState = getState(); // 如果释放成功 if (release(savedState)) { failed = false; return savedState; } else { throw new IllegalMonitorStateException(); } } finally { if (failed) // 节点的状态被设置成取消状态,从同步队列中移除 node.waitStatus = Node.CANCELLED; } } public final boolean release(int arg) { // 尝试获取锁,如果获取成功,唤醒后续线程 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } 线程唤醒后利用checkInterruptWhileWaiting方法检测中断模式 情况一中断发生时,线程还没有被唤醒过 这里假设已经发生过中断,则Thread.interrupted()方法必然返回true,接下来就是用transferAfterCancelledWait进一步判断是否发生了signal。 // 检查是否有中断,如果在发出信号之前被中断,则返回THROW_IE; // 在发出信号之后,则返回REINTERRUPT;如果没有被中断,则返回0。 private int checkInterruptWhileWaiting(Node node) { return Thread.interrupted() ? (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0; } final boolean transferAfterCancelledWait(Node node) { if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { enq(node); return true; } while (!isOnSyncQueue(node)) Thread.yield(); return false; } 只要一个节点的waitStatus还是Node.CONDITION,那就说明它还没有被signal过。 由于现在我们分析情况1,则当前节点的waitStatus必然是Node.CONDITION,则会成功执行compareAndSetWaitStatus(node, Node.CONDITION, 0),将该节点的状态设置成0,然后调用enq(node)方法将当前节点添加进同步队列中,然后返回true。 注意: 我们此时并没有断开node的nextWaiter,所以最后一定不要忘记将这个链接断开。 再回到transferAfterCancelledWait调用处,可知,由于transferAfterCancelledWait将返回true,现在checkInterruptWhileWaiting将返回THROW_IE,这表示我们在离开await方法时应当要抛出THROW_IE异常。 // .... while (!isOnSyncQueue(node)) { LockSupport.park(this); // 线程挂起的地方 // 线程将在这里被挂起,停止运行 // 能执行到这里说明要么是signal方法被调用了,要么是线程被中断了 // 所以检查下线程被唤醒的原因,如果是因为中断被唤醒,则跳出while循环 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } // 线程将在同步队列中利用进行acquireQueued方法进行“阻塞式”争锁, // 抢到锁就返回,抢不到锁就继续被挂起。因此,当await()方法返回时, // 必然是保证了当前线程已经持有了lock锁 // 我们这里假设它获取到了锁了,由于我们这时 // 的interruptMode = THROW_IE,则会跳过if语句。 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; // 遍历链表了,把链表中所有没有在等待的节点都拿出去,所以这里调用 // unlinkCancelledWaiters方法,该方法我们在前面await()第一部分的分析 // 的时候已经讲过了,它就是简单的遍历链表,找到所有waitStatus // 不为CONDITION的节点,并把它们从队列中移除 if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); // 这里我们的interruptMode=THROW_IE,说明发生了中断, // 则将调用reportInterruptAfterWait if (interruptMode != 0) reportInterruptAfterWait(interruptMode); } } // 在interruptMode=THROW_IE时,就是简单的抛出了一个InterruptedException private void reportInterruptAfterWait(int interruptMode) throws InterruptedException { if (interruptMode == THROW_IE) throw new InterruptedException(); else if (interruptMode == REINTERRUPT) selfInterrupt(); } interruptMode现在为THROW_IE,则我们将执行break,跳出while循环。接下来我们将执行acquireQueued(node, savedState)进行争锁,注意,这里传入的需要获取锁的重入数量是savedState,即之前释放了多少,这里就需要再次获取多少 情况一总结: 线程因为中断,从挂起的地方被唤醒 随后,我们通过transferAfterCancelledWait确认了线程的waitStatus值为Node.CONDITION,说明并没有signal发生过 然后我们修改线程的waitStatus为0,并通过enq(node)方法将其添加到同步队列中 接下来线程将在同步队列中以阻塞的方式获取,如果获取不到锁,将会被再次挂起 线程在同步队列中获取到锁后,将调用unlinkCancelledWaiters方法将自己从条件队列中移除,该方法还会顺便移除其他取消等待的锁 最后我们通过reportInterruptAfterWait抛出了InterruptedException 因此: 由此可以看出,一个调用了await方法挂起的线程在被中断后不会立即抛出InterruptedException,而是会被添加到同步队列中去争锁,如果争不到,还是会被挂起; 只有争到了锁之后,该线程才得以从同步队列和条件队列中移除,最后抛出InterruptedException。 所以说,一个调用了await方法的线程,即使被中断了,它依旧还是会被阻塞住,直到它获取到锁之后才能返回,并在返回时抛出InterruptedException。中断对它意义更多的是体现在将它从条件队列中移除,加入到同步队列中去争锁,从这个层面上看,中断和signal的效果其实很像,所不同的是,在await()方法返回后,如果是因为中断被唤醒,则await()方法需要抛出InterruptedException异常,表示是它是被非正常唤醒的(正常唤醒是指被signal唤醒)。 情况二中断发生时,线程已经被唤醒过包含以下两种情况 a. 被唤醒时,已经发生了中断,但此时线程已经被signal过了 final boolean transferAfterCancelledWait(Node node) { // 线程A执行到这里,CAS操作将会失败 if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { enq(node); return true; } // 由于中断发生前,线程已经被signal了,则这里只需要等待线程成功进入同步即可 while (!isOnSyncQueue(node)) Thread.yield(); return false; } 由于transferAfterCancelledWait返回了false,则checkInterruptWhileWaiting方法将返回REINTERRUPT,这说明我们在退出该方法时只需要再次中断因为signal后条件队列加入到了同步队列中所以node.nextWaiter为空了,所以直接走到了reportInterruptAfterWait(interruptMode)方法 if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); // 这里我们的interruptMode=THROW_IE,说明发生了中断, // 则将调用reportInterruptAfterWait if (interruptMode != 0) reportInterruptAfterWait(interruptMode); } } // 在interruptMode=THROW_IE时,就是简单的抛出了一个InterruptedException private void reportInterruptAfterWait(int interruptMode) throws InterruptedException { if (interruptMode == THROW_IE) throw new InterruptedException(); // 这里并没有抛出中断异常,而只是将当前线程再中断一次。 else if (interruptMode == REINTERRUPT) selfInterrupt(); } 情况二中的第一种情况总结: 线程从挂起的地方被唤醒,此时既发生过中断,又发生过signal 随后,我们通过transferAfterCancelledWait确认了线程的waitStatus值已经不为Node.CONDITION,说明signal发生于中断之前 然后,我们通过自旋的方式,等待signal方法执行完成,确保当前节点已经被成功添加到同步队列中 接下来线程将在同步队列中以阻塞的方式获取锁,如果获取不到,将会被再次挂起 最后我们通过reportInterruptAfterWait将当前线程再次中断,但是不会抛出InterruptedException b. 被唤醒时,并没有发生中断,但是在抢锁的过程中发生了中断 此情况就是已经被唤醒了那么isOnSyncQueue(node)返回true,在同步队列中了就,退出了while循环。 退出while循环后接下来还是利用acquireQueued争锁,因为前面没有发生中断,则interruptMode=0,这时,如果在争锁的过程中发生了中断,则acquireQueued将返回true,则此时interruptMode将变为REINTERRUPT。 接下是判断node.nextWaiter != null,由于在调用signal方法时已经将节点移出了队列,所有这个条件也不成立。 最后就是汇报中断状态了,此时interruptMode的值为REINTERRUPT,说明线程在被signal后又发生了中断,这个中断发生在抢锁的过程中,这个中断来的太晚了,因此我们只是再次自我中断一下。 情况二中的第二种情况总结: 线程被signal方法唤醒,此时并没有发生过中断 因为没有发生过中断,我们将从checkInterruptWhileWaiting处返回,此时interruptMode=0 接下来我们回到while循环中,因为signal方法保证了将节点添加到同步队列中,此时while循环条件不成立,循环退出 接下来线程将在同步队列中以阻塞的方式获取,如果获取不到锁,将会被再次挂起 线程获取到锁返回后,我们检测到在获取锁的过程中发生过中断,并且此时interruptMode=0,这时,我们将interruptMode修改为REINTERRUPT 最后我们通过reportInterruptAfterWait将当前线程再次中断,但是不会抛出InterruptedException 3.情况三一直没发生中断 直接正常返回 await方法总结 进入await()时必须是已经持有了锁 离开await()时同样必须是已经持有了锁 调用await()会使得当前线程被封装成Node扔进条件队列,然后释放所持有的锁 释放锁后,当前线程将在条件队列中被挂起,等待signal或者中断 线程被唤醒后会将会离开条件队列进入同步队列中进行抢锁 若在线程抢到锁之前发生过中断,则根据中断发生在signal之前还是之后记录中断模式 线程在抢到锁后进行善后工作(离开条件队列,处理中断异常) 线程已经持有了锁,从await()方法返回 在这里插入图片描述 在这一过程中我们尤其要关注中断,如前面所说,中断和signal所起到的作用都是将线程从条件队列中移除,加入到同步队列中去争锁,所不同的是,signal方法被认为是正常唤醒线程,中断方法被认为是非正常唤醒线程,如果中断发生在signal之前,则我们在最终返回时,应当抛出InterruptedException;如果中断发生在signal之后,我们就认为线程本身已经被正常唤醒了,这个中断来的太晚了,我们直接忽略它,并在await()返回时再自我中断一下,这种做法相当于将中断推迟至await()返回时再发生。 2. awaitUninterruptibly public final void awaitUninterruptibly() { Node node = addConditionWaiter(); int savedState = fullyRelease(node); boolean interrupted = false; while (!isOnSyncQueue(node)) { LockSupport.park(this); if (Thread.interrupted()) // 发生了中断后线程依旧留在了条件队列中,将会再次被挂起 interrupted = true; } if (acquireQueued(node, savedState) || interrupted) selfInterrupt(); } 由此可见,awaitUninterruptibly()全程忽略中断,即使是当前线程因为中断被唤醒,该方法也只是简单的记录中断状态,然后再次被挂起(因为并没有并没有任何操作将它添加到同步队列中)要使当前线程离开条件队列去争锁,则必须是发生了signal事件。 最后,当线程在获取锁的过程中发生了中断,该方法也是不响应,只是在最终获取到锁返回时,再自我中断一下。可以看出,该方法和“中断发生于signal之后的”REINTERRUPT模式的await()方法很像 方法总结: 中断虽然会唤醒线程,但是不会导致线程离开条件队列,如果线程只是因为中断而被唤醒,则他将再次被挂起 只有signal方法会使得线程离开条件队列 调用该方法时或者调用过程中如果发生了中断,仅仅会在该方法结束时再自我中断以下,不会抛出InterruptedException 3. awaitNanos 该方法几乎和await()方法一样,只是多了超时时间的处理该方法的主要设计思想是,如果设定的超时时间还没到,我们就将线程挂起;超过等待的时间了,我们就将线程从条件队列转移到同步对列中。 public final long awaitNanos(long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); final long deadline = System.nanoTime() + nanosTimeout; int interruptMode = 0; while (!isOnSyncQueue(node)) { if (nanosTimeout <= 0L) { transferAfterCancelledWait(node); break; } if (nanosTimeout >= spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; nanosTimeout = deadline - System.nanoTime(); } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); return deadline - System.nanoTime(); } 4. await(long time, TimeUnit unit) 在awaitNanos(long nanosTimeout)的基础上多了对于超时时间的时间单位的设置,但是在内部实现上还是会把时间转成纳秒去执行。 可以看出,这两个方法主要的差别就体现在返回值上面,awaitNanos(long nanosTimeout)的返回值是剩余的超时时间,如果该值大于0,说明超时时间还没到,则说明该返回是由signal行为导致的,而await(long time, TimeUnit unit)的返回值就是transferAfterCancelledWait(node)的值,我们知道,如果调用该方法时,node还没有被signal过则返回true,node已经被signal过了,则返回false。因此当await(long time, TimeUnit unit)方法返回true,则说明在超时时间到之前就已经发生过signal了,该方法的返回是由signal方法导致的而不是超时时间。 public final boolean await(long time, TimeUnit unit) throws InterruptedException { long nanosTimeout = unit.toNanos(time); if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); final long deadline = System.nanoTime() + nanosTimeout; boolean timedout = false; int interruptMode = 0; while (!isOnSyncQueue(node)) { if (nanosTimeout <= 0L) { timedout = transferAfterCancelledWait(node); break; } if (nanosTimeout >= spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; nanosTimeout = deadline - System.nanoTime(); } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); return !timedout; } 5. awaitUntil awaitUntil(Date deadline)方法与上面的几种带超时的方法也基本类似,所不同的是它的超时时间是一个绝对的时间 public final boolean awaitUntil(Date deadline) throws InterruptedException { long abstime = deadline.getTime(); if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); boolean timedout = false; int interruptMode = 0; while (!isOnSyncQueue(node)) { if (System.currentTimeMillis() > abstime) { timedout = transferAfterCancelledWait(node); break; } LockSupport.parkUntil(this, abstime); 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); return !timedout; } 6. signal 只唤醒一个节点 public final void signal() { // getExclusiveOwnerThread() == Thread.currentThread(); 当前线 // 程是不是独占线程 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); // 获取第一个阻塞线程节点 Node first = firstWaiter; // 条件队列是否为空 if (first != null) doSignal(first); } // 遍历整个条件队列,找到第一个没有被cancelled的节点,并将它添加到条件队列的末尾 // 如果条件队列里面已经没有节点了,则将条件队列清空 private void doSignal(Node first) { do { // 将firstWaiter指向条件队列队头的下一个节点 if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; // 将条件队列原来的队头从条件队列中断开,则此时该节点成为一个孤立的节点 first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); } 方法总结: 调用signal()方法会从当前条件队列中取出第一个没有被cancel的节点添加到sync队列的末尾。 7. signalAll 唤醒所有的节点 public final void signalAll() { // getExclusiveOwnerThread() == Thread.currentThread(); 当前线 // 程是不是独占线程 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); // 获取第一个阻塞线程节点 Node first = firstWaiter; // 条件队列是否为空 if (first != null) doSignalAll(first); } // 移除并转移所有节点 private void doSignalAll(Node first) { // 清空队列中所有数据 lastWaiter = firstWaiter = null; do { Node next = first.nextWaiter; first.nextWaiter = null; transferForSignal(first); first = next; } while (first != null); } // 将条件队列中的节点一个一个的遍历到同步队列中 final boolean transferForSignal(Node node) { // 如果该节点在调用signal方法前已经被取消了,则直接跳过这个节点 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; // 利用enq方法将该节点添加至同步队列的尾部 Node p = enq(node); // 返回的是前驱节点,将其设置SIGNAL之后,才会挂起 // 当前节点 int ws = p.waitStatus; if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; } 在transferForSignal方法中,我们先使用CAS操作将当前节点的waitStatus状态由CONDTION设为0,如果修改不成功,则说明该节点已经被CANCEL了,则我们直接返回,操作下一个节点;如果修改成功,则说明我们已经将该节点从等待的条件队列中成功“唤醒”了,但此时该节点对应的线程并没有真正被唤醒,它还要和其他普通线程一样去争锁,因此它将被添加到同步队列的末尾等待获取锁 。 方法总结: 将条件队列清空(只是令lastWaiter = firstWaiter = null,队列中的节点和连接关系仍然还存在) 将条件队列中的头节点取出,使之成为孤立节点(nextWaiter,prev,next属性都为null) 如果该节点处于被Cancelled了的状态,则直接跳过该节点(由于是孤立节点,则会被GC回收) 如果该节点处于正常状态,则通过enq方法将它添加到同步队列的末尾 判断是否需要将该节点唤醒(包括设置该节点的前驱节点的状态为SIGNAL),如有必要,直接唤醒该节点 重复2-5,直到整个条件队列中的节点都被处理完 6. 总结 以上便是Condition的分析,下一篇文章将是并发容器类的分析,如有错误之处,帮忙指出及时更正,谢谢, 如果喜欢谢谢点赞加收藏加转发(转发注明出处谢谢!!!) 你可能感兴趣的:(并发条件队列之Condition 精讲) 【ROS2】RViz2自定义面板插件(rviz_common::Panel)的详细步骤 郭老二 ROSQtROS2 【ROS】郭老二博文之:ROS目录1、简述RViz2的插件基于ROS2的插件库(pluginlib)机制,通过动态加载共享库实现功能扩展。注意:RViz2使用QT作为UI框架,虽然QT也有插件机制,但是RViz2并没有使用QT的插件机制,而是通过pluginlib加载功能模块来实现。2、插件类型每个插件必须继承相应的基类,才能被RViz识别。RViz2中共有5类插件:插件类型基类Display(显 C++14新特性之lambda参数auto 画个逗号给明天" C++14新特性c++开发语言 1.介绍在C++11中,lambda表达式参数需要使用具体的类型,例如:autof=[](inta){returna;}参数的类型为int。在C++14中对lambda表达式进行了优化,参数可以是auto,例如:autof=[](autoa){returna;};这使得lambda表达式更加的灵活,可以接收任意类型的参数,这一特性通常称为泛型lambda。2.使用场景(1)结合STL算法。#inc 编程题-在排序数组中查找元素的第一个和最后一个位置(中等) Kevin Kou 数据结构算法c++二分查找 题目:给你一个按照非递减顺序排列的整数数组nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target,返回[-1,-1]。你必须设计并实现时间复杂度为O(logn)的算法解决此问题。解法一(二分查找):直接遍历所有数组nums中元素时间复杂度为O(n),没有利用到数组升序排列的条件。由于数组已经排序,因此整个数组是单调递增的,我们可以利用 es凌晨自己把索引删除了,包括es自己的索引 小码农吗 日常栏目elasticsearch大数据搜索引擎 如果Elasticsearch(ES)在凌晨自动删除了包括自身索引在内的大量索引,这是一个比较严重的问题,下面从多个方面分析可能的原因,并给出排查步骤与相应命令。可能的原因1.索引生命周期管理(ILM)策略ILM允许根据索引的年龄、大小等条件自动管理索引的生命周期,若策略配置不当,可能导致凌晨触发删除操作。2.磁盘压力触发清理当磁盘空间达到一定阈值,ES可能会自动删除一些索引以释放空间。3.自动快 计算机网络之广域网(PPP协议) DKPT #计算机网络计算机网络开发语言算法学习笔记 一、定义与用途PPP协议主要用于在两个网络节点之间建立直接连接,并在此连接上进行数据传输。它是为在同等单元之间传输数据包这样的简单链路而设计的,这种链路提供全双工操作,并按照顺序传递数据包。PPP协议是各类型主机、网桥和路由器之间简单连接的一种共通解决方案。二、组成部分PPP协议由三个主要部分组成:数据帧封装方法:用于定义数据在传输过程中的格式。链路控制协议LCP(LinkControlProto CAS 的工作原理 冰糖心158 Java开发2025Java面试系列java CAS(Compare-and-Swap)本身并不是一个独立的项目或软件,而是一种底层的硬件指令和并发编程概念1.核心概念CAS是一种原子操作:它的“比较”和“交换”这两个动作是作为一个不可分割的整体执行的,要么都成功,要么都失败,不会出现中间状态。CAS是一种无锁操作(乐观锁):它在操作过程中不会阻塞线程,而是通过不断重试来实现同步。CAS操作三个数:内存位置(V):要读取和修改的内存地址。预期 Java进阶之泛型 m0_74824483 面试学习路线阿里巴巴java开发语言 泛型(Generics)定义泛型:允许在定义类、接口和方法时使用类型参数,从而在编译时捕获类型错误,提高代码的类型安全性和复用性。主要用途类型安全:避免类型转换错误,编译时检查类型安全。代码复用:通过泛型可以编写通用的类和方法,适用于多种数据类型。消除强制类型转换:在使用泛型时,编译器会自动进行类型转换,减少代码中的强制类型转换。泛型的基本语法标记符T是类型参数,通常使用T、E、K、V等字母来表示 QT模型/视图结构之概述 华衣在盛 Qt5学习笔记qt开发语言 引言Qt的模型视图结构分为三部分,模型(model)、视图(view)、代理(Delegate)。其中,模型与数据源通信,并为其他不见提供接口;而视图从模型中获得用来引用数据条目的模型索引(ModelIndex)。在视图中,代理负责绘制绘制数据条目,当编辑条目时,代理和模型直接进行通信。模型/视图/代理之间通过信号和槽进行通信。它们之间的关系如下:数据发生变化时,模型发出信号通知视图。用户对界面进 结合实际讲NR系列2—— SIB1 写代码写到手抽筋 NR学习实战复盘网络5G 这是在基站抓取的sib1的一条信令L3MessageContentBCCH-DL-SCH-Messagemessagec1systemInformationBlockType1cellSelectionInfoq-RxLevMin:-64q-QualMin:-19cellAccessRelatedInfoplmn-IdentityListPLMN-IdentityInfoplmn-Identity Apache SeaTunnel 社区2025年全年计划公布,共同构建下一代数据集成生态 数据库 ApacheSeaTunnel社区正在全球范围内寻找热爱开源、乐于分享的技术先锋!无论你是开发者、用户、布道者还是行业专家,这里都有属于你的舞台。欢迎社区有志之士加入我们,一起推动开源数据集成工具的创新与发展!ApacheSeaTunnel社区全年活动规划一览:https://gzg9x067ms.feishu.cn/base/Hnp1bIKqLaAaTQsqzKscMJ0OnFd...申请流程: Day 4:API高级开发指南 翻晒时光 DeepSeekpythonai 目标:构建高可用、高并发的企业级API服务系统一、流式响应工程化实践1.1流式传输核心机制技术架构对比:传输方式延迟显存占用适用场景标准响应2.1s12GB短文本生成流式响应首包300ms4GB长文档/实时交互Python异步流处理:importasynciofromdeepseekimportAsyncDeepSeekasyncdefstream_response(prompt):client= SpringBoot服务器的采购上,服务器的数量和性能配置如何取舍【无标题】 陈老师还在写代码 SpringBoot100问服务器运维 在采购SpringBoot服务器时,需根据应用需求、预算和扩展性等因素综合考虑服务器的数量和性能配置。以下是一些关键点:1.应用需求分析用户量:用户量越大,需要的服务器性能和数量越多。请求频率:高并发请求需要更强的CPU和内存支持。数据处理:数据密集型应用需要更高的I/O性能和存储容量。响应时间:低延迟要求需要更快的CPU和更大的内存。2.性能配置CPU:高并发或复杂计算需要多核CPU。内存:内存 史上最全JAVA八股文——redis篇——缓存篇,欢迎收藏 Ethan Yankang java开发语言 个人主页所有八股思维导图面试八股之Redis篇1——缓存-CSDN博客面试八股之Redis篇1.1——缓存——什么是缓存穿透?怎么解决?-CSDN博客面试八股之Redis篇1.2——缓存——什么是缓存击穿?怎么解决?-CSDN博客面试八股之Redis篇1.3——缓存——什么是缓存雪崩?怎么解决?-CSDN博客面试八股之Redis篇1.4——缓存——打油诗《缓存三兄弟》-CSDN博客面试八股之Red 为AI聊天工具添加一个知识系统 之100详细设计之41 自性三藏 一水鉴天 人工智能 本文要点法宝和三藏--今天的题目。总括如下:三藏[经/律/论]法宝:法阵/法轮/法力(三“件”证件/表件/物件,分别对应三藏:论藏/律藏/经藏--反序)。“法宝”演示了发轮轮转的法阵中物件具有的法力。这里的“法宝”用来揭示“量”的“非”,其表现为“非量”。比如,揭示了“物件”的“法力”的“经藏”保存了“物质”的非物理部分,即一切非物质遗产。三藏(经藏/律藏/论藏--正序)在本项目(为使用AI聊天工 Win10如何批量修改文件后缀名?Win10批量修改文件后缀名的方法 shengyin714959 最高笔记笔记笔记 电脑文件都有一个固定的格式,并且每个格式都代表着不同的文件类型,但是有时候我们需要批量修改文件的后缀名要如何去操作呢?下面就让小编和大家来讲一讲Win10批量修改文件后缀名的方法。Win10批量修改文件后缀名的方法1、右键选择文件“属性”,即可查看文件的类型。2、点击左下角的“开始键-控制面板-文件夹选项”,然后点击“查看”,找到“隐藏已知文件夹类型的扩展名”取消勾选,点击应用,就可以直接显示文件 13 | MySQL主从数据库同步是如何实现的? _Rye_ 后端存储数据库mysql 回顾我们之前讲MySQL相关的几节,会发现主从同步有多重要。解决数据可靠性的问题需要用到主从同步;解决MySQL服务高可用要用到主从同步;应对高并发的时候,还是要用到主从同步。我们在运维MySQL集群时,遇到的很多常见的问题,比如说,为什么从节点故障会影响到主节点?为什么主从切换之后丢数据了?为什么明明没有更新数据,客户端读到的数据还是变来变去的?这些都和主从同步的配置有密切的关系。不但要理解My Kivy教程大全之 使用 NumPy 和 Kivy 对 Android 设备进行图像分类 知识大胖 Python源码大全pythonkivynumpy 文章简介ANN架构。使用KV语言创建小部件树。创建Kivy应用程序。使用正确的NumPy版本。构建Android应用程序。了解更多信息本教程的重点是构建一个调用预训练的ANN来对图像进行分类的Android应用程序。这里不深入讨论准备数据集、构建、训练和优化ANN的步骤。在本教程中将仅对它们进行简要讨论。但不要担心——在不了解这些细节的情况下遵循本教程中的想法是可以的。如果您想了解它们,请查看我之 SpringBoot依赖之PostgreSQL Driver集成 ahauedu 微服务架构设计springbootpostgresql后端 概念PostgreSQLDriverPostgreSQL是一个强大、开源的对象关系型数据库管理系统(ORDBMS),适用于大数量处理、复杂的应用程序、数据分析和BI、金融以及电子商务领域。PostgreSQLDriver依赖名称:PostgreSQLDriver功能描述:AJDBCandR2DBCdriverthatallowsJavaprogramstoconnecttoaPostgreSQLd PCDN的虚拟机与云主机区别 黑石云 边缘计算 使用虚拟机和云主机运行PCDN(P2PCDN)时,主要存在以下区别:一、资源分配与灵活性虚拟机:资源受限:虚拟机运行在物理服务器上,其资源(如CPU、内存、带宽)受到物理服务器资源的限制。扩展性较差:当需要增加资源以应对更大的流量或更高的并发请求时,虚拟机可能需要重新配置或迁移到更强大的物理服务器上,这通常涉及较多的手动操作和较长的准备时间。云主机:资源弹性分配:云主机基于云计算架构,可以根据实际 接口自动化测试框架之——关于接口关联的封装 测试也要努力 Yamlpython接口自动化 思路:去掉全局变量(避免每次使用时需要导包),用YAML文件代替保存(对YAML进行读写等操作)。新建一个公共包common,在其下新建yaml_util.py,写入三个方法:读、写、清空importosimportyaml##读取yaml,读取yaml时,传入key来读取对应的值defread_yaml(key):withopen(os.getcwd()+'/extract.yaml')asf: 接口自动化测试之request模块讲解,以及初步接口自动化测试框架封装(统一请求) 蜗牛_Chenpangzi 测试json自动化python 一、有接口测试工具的情况下,为什么要做接口自动化?1.敏捷开发,接口一般数量很大,团队实现接测试,版本控制。2.功能太死板,有些接口完全无法实现(复杂的加密接口,签名接口等)3.接口项目当中有多种不同协议的接口。4.排错,定位接口问题不方便,结合抓包实现。5.没有办法生成美观的报告。6.多接口串联,数据库验证,日志监控。7.有些公司做web自动化+接口自动化。二、python+requests模块 js如何实现异步 weixin_43645783 基础javascriptjavascript前端 同步与异步:同步是指按照代码的书写顺序一行行的去执行,上一段代码执行完毕才能执行下一段代码异步可以理解为一种并行的处理方式,不必等待一个程序执行完就可以执行其他的任务JavaScript需要异步是因为JavaScript是单线程运行的,JavaScript中常用的异步场景有定时器,Ajax请求,事件绑定。Js是如何实现异步的:Js先执行同步代码,将异步代码推入到任务队列中,js是单线程的,但又能实 Android网络技术——HttpUrlConnection和OkHttp penghc_xhs Android第一行代码android Android网络技术——HttpUrlConnection和OkHttpHttpURLConnection是一个abstract类,可用于发起网络请求OkHttp不仅在接口封装上做得简单易用,就连在底层实现上也是自成一派,比起原生的HttpURLConnection,可以说是有过之而无不及,现在已经成了广大Android开发者首选的网络通信库一、布局设置注:ScrollView容器用于滚动内部的 python+pytest接口自动化之测试函数、测试类/测试方法的封装 美团程序员 自动化测试软件测试面试面试pythonpytest自动化 前言今天呢,笔者想和大家聊聊python+pytest接口自动化中将代码进行封装,只有将测试代码进行封装,才能被测试框架识别执行。例如单个接口的请求代码如下:importrequestsheaders={"user-agent":"Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/99.0.4 红队攻防渗透技术实战流程:云安全之云原生安全:K8s污点横向移动 HACKNOE 红队攻防渗透技术研习室k8s安全 红队云攻防实战1.云原生安全-K8s安全-Kubelet漏洞利用1.1K8s安全-横向移动-污点Taint-概念1.2K8s安全-横向移动-污点Taint实战1.2.2K8s安全-横向移动-探针APIServer未授权1.2.2K8s安全-横向移动-利用污点Taint横向移动master节点1.2.3K8s安全-Master节点漏洞利用-config横向移动节点1.云原生安全-K8s安全-Kube 红队攻防渗透技术实战流程:云安全之云原生安全:K8s搭建及节点漏洞利用 HACKNOE 红队攻防渗透技术研习室云原生安全kubernetes 红队云攻防实战1.云原生-K8s安全-名词架构&各攻击点1.1云原生-K8s安全-概念1.2云原生-K8s安全-K8S集群架构解释1.2.1K8s安全-K8S集群架构-Master节点1.2.2K8s安全-K8S集群架构-Node节点1.2.3K8s安全-K8S集群架构-Pod容器1.3云原生安全-K8s安全-K8S集群攻击点`(重点)`2.云原生安全-K8s安全-K8S集群环境搭建3.云原生安全 【Python自动化测试25】接口自动化测试实战五_数据库断言、接口关联及相关管理优化 萌笑天 Python自动化测试python自动化开发语言自动化测试软件测试 文章目录一、前言二、校验数据库、接口关联及项目优化一、前言 本文章主要会讲解接口自动化测试中Python的数据库断言以及相关的接口关联的测试,除此之外下方有系列文章的传送门,还在持续更新中,感兴趣的小伙伴也可以前往查看,话不多说,让我们一起看看吧~系列文章: 系列文章1:【Python自动化测试1】遇见Python之美 系列文章2:【Python自动化测试2】Python安装配置及PyCha Android 访问网络框架之——OkHttp框架的解析 mr丶yang 原创Okhttp框架网络 越来越发现一些第三方的框架比Android原生大的API好用多了,而且android废弃掉了HttpClient,有必要学习一些访问网络的框架,于是踏上了一条框架学习之路,先前学习了Volley框架。今天来解析一下OkHttp框架。一,OkHttp的简单使用,主要包括:一般的get请求一般的post请求基于Http的文件上传文件下载加载图片支持请求回调,直接返回对象、对象集合支持session的保 接口自动化测试实战之Python操作数据库、接口关联及相关管理优化 程序员潇潇 软件测试数据库pythonoracle软件测试自动化测试功能测试程序人生 一、前言本文章主要会讲解接口自动化测试中Python如何操作数据库、为何要操作数据库,有哪些利弊,以及数据库断言、相关的接口关联的测试二、自动化数据库理论与操作2.1接口自动化为何要操作数据库接口自动化中操作数据库主要是根据业务层面决定的,部分情况例如查询手机号、或个人信息时需要操作数据库,有时候也有可能需要删除某个内容,通常而言不会这么做罢了。2.2接口自动化操作数据库的利弊"""利:1、能够根 JVM垃圾回收器之深入理解CMS垃圾回收器 lance小码匠 JVM面试题java基础javaCMS面试 前言CMS垃圾回收器是本人理解最深刻的JVM垃圾回收器,CMS是首个可以与用户线程并发的低停顿收集器。随着技术的发展、JDK的更新迭代,CMS这个曾经被寄予厚望的并发垃圾回收器已经慢慢要被时代抛弃了,后面出来的G1,ZGC已经盖过了CMS的光芒,JDK9之后CMS甚至被抛弃为不建议使用。但是后来者都是踩在CMS肩膀上迭代的,你可以在G1中看到大量CMS代码的影子,同时现在国内很多公司依旧还在使用J ASM系列六 利用TreeApi 添加和移除类成员 lijingyao8206 jvm动态代理ASM字节码技术TreeAPI 同生成的做法一样,添加和移除类成员只要去修改fields和methods中的元素即可。这里我们拿一个简单的类做例子,下面这个Task类,我们来移除isNeedRemove方法,并且添加一个int 类型的addedField属性。 package asm.core; /** * Created by yunshen.ljy on 2015/6/ Springmvc-权限设计 bee1314 springWebjsp 万丈高楼平地起。 权限管理对于管理系统而言已经是标配中的标配了吧,对于我等俗人更是不能免俗。同时就目前的项目状况而言,我们还不需要那么高大上的开源的解决方案,如Spring Security,Shiro。小伙伴一致决定我们还是从基本的功能迭代起来吧。 目标: 1.实现权限的管理(CRUD) 2.实现部门管理 (CRUD) 3.实现人员的管理 (CRUD) 4.实现部门和权限 算法竞赛入门经典(第二版)第2章习题 CrazyMizzz c算法 2.4.1 输出技巧 #include <stdio.h> int main() { int i, n; scanf("%d", &n); for (i = 1; i <= n; i++) printf("%d\n", i); return 0; } 习题2-2 水仙花数(daffodil struts2中jsp自动跳转到Action 麦田的设计者 jspwebxmlstruts2自动跳转 1、在struts2的开发中,经常需要用户点击网页后就直接跳转到一个Action,执行Action里面的方法,利用mvc分层思想执行相应操作在界面上得到动态数据。毕竟用户不可能在地址栏里输入一个Action(不是专业人士) 2、<jsp:forward page="xxx.action" /> ,这个标签可以实现跳转,page的路径是相对地址,不同与jsp和j php 操作webservice实例 IT独行者 PHPwebservice 首先大家要简单了解了何谓webservice,接下来就做两个非常简单的例子,webservice还是逃不开server端与client端。我测试的环境为:apache2.2.11 php5.2.10做这个测试之前,要确认你的php配置文件中已经将soap扩展打开,即extension=php_soap.dll; OK 现在我们来体验webservice //server端 serve Windows下使用Vagrant安装linux系统 _wy_ windowsvagrant 准备工作: 下载安装 VirtualBox :https://www.virtualbox.org/ 下载安装 Vagrant :http://www.vagrantup.com/ 下载需要使用的 box : 官方提供的范例:http://files.vagrantup.com/precise32.box 还可以在 http://www.vagrantbox.es/ 更改linux的文件拥有者及用户组(chown和chgrp) 无量 clinuxchgrpchown 本文(转) http://blog.163.com/yanenshun@126/blog/static/128388169201203011157308/ http://ydlmlh.iteye.com/blog/1435157 一、基本使用: 使用chown命令可以修改文件或目录所属的用户: 命令 linux下抓包工具 矮蛋蛋 linux 原文地址: http://blog.chinaunix.net/uid-23670869-id-2610683.html tcpdump -nn -vv -X udp port 8888 上面命令是抓取udp包、端口为8888 netstat -tln 命令是用来查看linux的端口使用情况 13 . 列出所有的网络连接 lsof -i 14. 列出所有tcp 网络连接信息 l 我觉得mybatis是垃圾!:“每一个用mybatis的男纸,你伤不起” alafqq mybatis 最近看了 每一个用mybatis的男纸,你伤不起 原文地址 :http://www.iteye.com/topic/1073938 发表一下个人看法。欢迎大神拍砖; 个人一直使用的是Ibatis框架,公司对其进行过小小的改良; 最近换了公司,要使用新的框架。听说mybatis不错;就对其进行了部分的研究; 发现多了一个mapper层;个人感觉就是个dao; 解决java数据交换之谜 百合不是茶 数据交换 交换两个数字的方法有以下三种 ,其中第一种最常用 /* 输出最小的一个数 */ public class jiaohuan1 { public static void main(String[] args) { int a =4; int b = 3; if(a<b){ // 第一种交换方式 int tmep = 渐变显示 bijian1013 JavaScript <style type="text/css"> #wxf { FILTER: progid:DXImageTransform.Microsoft.Gradient(GradientType=0, StartColorStr=#ffffff, EndColorStr=#97FF98); height: 25px; } </style> 探索JUnit4扩展:断言语法assertThat bijian1013 java单元测试assertThat 一.概述 JUnit 设计的目的就是有效地抓住编程人员写代码的意图,然后快速检查他们的代码是否与他们的意图相匹配。 JUnit 发展至今,版本不停的翻新,但是所有版本都一致致力于解决一个问题,那就是如何发现编程人员的代码意图,并且如何使得编程人员更加容易地表达他们的代码意图。JUnit 4.4 也是为了如何能够 【Gson三】Gson解析{"data":{"IM":["MSN","QQ","Gtalk"]}} bit1129 gson 如何把如下简单的JSON字符串反序列化为Java的POJO对象? {"data":{"IM":["MSN","QQ","Gtalk"]}} 下面的POJO类Model无法完成正确的解析: import com.google.gson.Gson; 【Kafka九】Kafka High Level API vs. Low Level API bit1129 kafka 1. Kafka提供了两种Consumer API High Level Consumer API Low Level Consumer API(Kafka诡异的称之为Simple Consumer API,实际上非常复杂) 在选用哪种Consumer API时,首先要弄清楚这两种API的工作原理,能做什么不能做什么,能做的话怎么做的以及用的时候,有哪些可能的问题 在nginx中集成lua脚本:添加自定义Http头,封IP等 ronin47 nginx lua Lua是一个可以嵌入到Nginx配置文件中的动态脚本语言,从而可以在Nginx请求处理的任何阶段执行各种Lua代码。刚开始我们只是用Lua 把请求路由到后端服务器,但是它对我们架构的作用超出了我们的预期。下面就讲讲我们所做的工作。 强制搜索引擎只索引mixlr.com Google把子域名当作完全独立的网站,我们不希望爬虫抓取子域名的页面,降低我们的Page rank。 location /{ java-归并排序 bylijinnan java import java.util.Arrays; public class MergeSort { public static void main(String[] args) { int[] a={20,1,3,8,5,9,4,25}; mergeSort(a,0,a.length-1); System.out.println(Arrays.to Netty源码学习-CompositeChannelBuffer bylijinnan javanetty CompositeChannelBuffer体现了Netty的“Transparent Zero Copy” 查看API( http://docs.jboss.org/netty/3.2/api/org/jboss/netty/buffer/package-summary.html#package_description) 可以看到,所谓“Transparent Zero Copy”是通 Android中给Activity添加返回键 hotsunshine Activity // this need android:minSdkVersion="11" getActionBar().setDisplayHomeAsUpEnabled(true); @Override public boolean onOptionsItemSelected(MenuItem item) { 静态页面传参 ctrain 静态 $(document).ready(function () { var request = { QueryString : function (val) { var uri = window.location.search; var re = new RegExp("" + val + "=([^&?]*)", & Windows中查找某个目录下的所有文件中包含某个字符串的命令 daizj windows查找某个目录下的所有文件包含某个字符串 findstr可以完成这个工作。 [html] view plain copy >findstr /s /i "string" *.* 上面的命令表示,当前目录以及当前目录的所有子目录下的所有文件中查找"string&qu 改善程序代码质量的一些技巧 dcj3sjt126com 编程PHP重构 有很多理由都能说明为什么我们应该写出清晰、可读性好的程序。最重要的一点,程序你只写一次,但以后会无数次的阅读。当你第二天回头来看你的代码 时,你就要开始阅读它了。当你把代码拿给其他人看时,他必须阅读你的代码。因此,在编写时多花一点时间,你会在阅读它时节省大量的时间。让我们看一些基本的编程技巧: 尽量保持方法简短 尽管很多人都遵 SharedPreferences对数据的存储 dcj3sjt126com SharedPreferences简介: &nbs linux复习笔记之bash shell (2) bash基础 eksliang bashbash shell 转载请出自出处: http://eksliang.iteye.com/blog/2104329 1.影响显示结果的语系变量(locale) 1.1locale这个命令就是查看当前系统支持多少种语系,命令使用如下: [root@localhost shell]# locale LANG=en_US.UTF-8 LC_CTYPE="en_US.UTF-8" Android零碎知识总结 gqdy365 android 1、CopyOnWriteArrayList add(E) 和remove(int index)都是对新的数组进行修改和新增。所以在多线程操作时不会出现java.util.ConcurrentModificationException错误。 所以最后得出结论:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。发生修改时候做copy,新老版本分离,保证读的高 HoverTree.Model.ArticleSelect类的作用 hvt Web.netC#hovertreeasp.net ArticleSelect类在命名空间HoverTree.Model中可以认为是文章查询条件类,用于存放查询文章时的条件,例如HvtId就是文章的id。HvtIsShow就是文章的显示属性,当为-1是,该条件不产生作用,当为0时,查询不公开显示的文章,当为1时查询公开显示的文章。HvtIsHome则为是否在首页显示。HoverTree系统源码完全开放,开发环境为Visual Studio 2013 PHP 判断是否使用代理 PHP Proxy Detector 天梯梦 proxy 1. php 类 I found this class looking for something else actually but I remembered I needed some while ago something similar and I never found one. I'm sure it will help a lot of developers who try to apache的math库中的回归——regression(翻译) lvdccyb Mathapache 这个Math库,虽然不向weka那样专业的ML库,但是用户友好,易用。 多元线性回归,协方差和相关性(皮尔逊和斯皮尔曼),分布测试(假设检验,t,卡方,G),统计。 数学库中还包含,Cholesky,LU,SVD,QR,特征根分解,真不错。 基本覆盖了:线代,统计,矩阵, 最优化理论 曲线拟合 常微分方程 遗传算法(GA), 还有3维的运算。。。 基础数据结构和算法十三:Undirected Graphs (2) sunwinner Algorithm Design pattern for graph processing. Since we consider a large number of graph-processing algorithms, our initial design goal is to decouple our implementations from the graph representation 云计算平台最重要的五项技术 sumapp 云计算云平台智城云 云计算平台最重要的五项技术 1、云服务器 云服务器提供简单高效,处理能力可弹性伸缩的计算服务,支持国内领先的云计算技术和大规模分布存储技术,使您的系统更稳定、数据更安全、传输更快速、部署更灵活。 特性 机型丰富 通过高性能服务器虚拟化为云服务器,提供丰富配置类型虚拟机,极大简化数据存储、数据库搭建、web服务器搭建等工作; 仅需要几分钟,根据CP 《京东技术解密》有奖试读获奖名单公布 ITeye管理员 活动 ITeye携手博文视点举办的12月技术图书有奖试读活动已圆满结束,非常感谢广大用户对本次活动的关注与参与。 12月试读活动回顾: http://webmaster.iteye.com/blog/2164754 本次技术图书试读活动获奖名单及相应作品如下: 一等奖(两名) Microhardest:http://microhardest.ite 按字母分类: ABCDEFGHIJKLMNOPQRSTUVWXYZ其他