And blocking queue scenarios

1. The application scenario of the blocking queue

 
Queuing

Let's look at a scene change, monitor file, we opened a thread gets change the file name, then we will do to get the resolution of a database, and so on other operations. If we count their processing time of these two operations separately, you will find time behind the resolution requires far greater than the time to get in front of the file name.

 
File monitoring and handling

Then if a thread is monitored file changes every second poll, assuming a time-consuming operation 1ms, time-consuming operation 2 may add up to exactly 1 and in a second operation, sometimes because it is too large, too long and exceeds the analytical the second, there will have to be monitored file has changed, but too late to deal with, so this batch file changes to be missed.

The above scene abstraction, is in the producer-consumer model, inconsistent production data and consumption data rates, faster data speeds if the production and consumption (processing), however, can lead to data loss. This time we can apply the blocking queue to solve this problem.

First blocking queue is a queue, we play a team single-threaded into the production data, consumption data from multiple threads. Since blocking queue characteristics: Consumer blocking team space-time, blocking the production team full time. Multithreading consumption data played a role in accelerating consumption, so that data does not produce excessive backlog in the team, while the production of processed data will not be lost.

2. The blocking queue collections

JDK7 offers 7 blocking queue. They are

  • ArrayBlockingQueue: a structure consisting of an array bounded blocking queue.
  • LinkedBlockingQueue: a linked list of structures bounded blocking queue.
  • PriorityBlockingQueue: a support prioritization of unbounded blocking queue.
  • DelayQueue: Use a priority queue unbounded blocking queue implementation.
  • SynchronousQueue: a blocking queue element is not stored.
  • LinkedTransferQueue: a list structure consisting of unbounded blocking queue.
  • LinkedBlockingDeque: a linked list structure consisting of two-way blocking queue.

ArrayBlockingQueue is a realization of an array bounded blocking queue. This queue orders elements FIFO principle (FIFO) is. By default, the visitor does not guarantee fair access to the queue, the so-called fair access to the queue means that all the producer or consumer threads thread is blocked when the queue is available, in accordance with the order blocking access to the queue, that is, first blocked the producer thread , you can insert elements into the queue, first blocked the consumer thread can acquire the elements start queue. Under normal circumstances in order to ensure fairness will reduce throughput. We can create a level playing field blocking queue using the following code:

ArrayBlockingQueue fairQueue = new  ArrayBlockingQueue(1000,true);

Fairness is used visitor reentrant lock achieved, as follows:

public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = new Object[capacity]; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); } 

LinkedBlockingQueue is a linked list to achieve bounded blocking queue. The default and maximum length of this queue is Integer.MAX_VALUE. This queue orders elements FIFO principle.

PriorityBlockingQueue is a priority queue unbounded support. By default, the element is in a natural order, the collation may be specified by the comparator element comparator. Elements in ascending order.

DelayQueue is a delay obtaining support elements of unbounded blocking queue. Queue using PriorityQueue to achieve. Queue element must implement Delayed interface, you can specify when you create an element how long it takes to get the current element from the queue. Only elements can be extracted from the queue when the delay has expired. We can DelayQueue used in the following scenarios:

  • Design caching system: You can use DelayQueue save validity cache elements, use a thread loop query DelayQueue, once DelayQueue acquired from the elements, represent a valid cached.
  • Timing task scheduling. Use task execution time DelayQueue save the day and will be executed once to obtain from DelayQueue the task started from such TimerQueue it is to use DelayQueue implemented.

Delayed queue must implement compareTo order to specify the element. For example, let's longest delay time at the end of the queue. Codes are as follows:

public int compareTo(Delayed other) { if (other == this) // compare zero ONLY if same object return 0; if (other instanceof ScheduledFutureTask) { ScheduledFutureTask x = (ScheduledFutureTask)other; long diff = time - x.time; if (diff < 0) return -1; else if (diff > 0) return 1; else if (sequenceNumber < x.sequenceNumber) return -1; else return 1; } long d = (getDelay(TimeUnit.NANOSECONDS) - other.getDelay(TimeUnit.NANOSECONDS)); return (d == 0) ? 0 : ((d < 0) ? -1 : 1); } 

How to achieve Delayed Interface

We can refer to ScheduledThreadPoolExecutor in ScheduledFutureTask class. This class implements Delayed interface. First of all: when the object is created using the time before recording an object when you can use the following code:

ScheduledFutureTask(Runnable r, V result, long ns, long period) {
            super(r, result);
            this.time = ns;
            this.period = period; this.sequenceNumber = sequencer.getAndIncrement(); } 

GetDelay can then use to query the current element also needs long delay, as follows:

public long getDelay(TimeUnit unit) { return unit.convert(time - now(), TimeUnit.NANOSECONDS); } 

As can be seen by the constructor of the delay time parameter ns nanosecond units, their design is preferably nanosecond time, since the unit can specify any time getDelay, once in nanoseconds as a unit, and the delay time and less accurate ns trouble. Note that when the time less than the current time, getDelay will return a negative number when in use.

How to achieve Latency Queuing

Latency Queuing implementation is very simple, when consumers get an element from the queue, if the element does not reach the delay time, it blocks the current thread.

long delay = first.getDelay(TimeUnit.NANOSECONDS);
                    if (delay <= 0)
                        return q.poll();
                    else if (leader != null) available.await(); 

SynchronousQueue is not a storage element of blocking queue. Put each operation must wait for a take, otherwise can not continue to add elements. SynchronousQueue can be seen as a passer, the producer is responsible for passing data directly to the consumer thread processing thread. Itself is not stored in any queue element, is very suitable for transmission scenario, such as the data used in a thread, the threads pass to another, higher throughput SynchronousQueue LinkedBlockingQueue and ArrayBlockingQueue.

LinkedTransferQueue is a linked list of structures TransferQueue unbounded blocking queue. Compared to other blocking queue, LinkedTransferQueue more tryTransfer and transfer methods.

transfer method. (When consumers take (or method with a time limit of poll) () method) if consumers are currently waiting to receive the element, transfer method can producers element passed immediately transfer (transport) to the consumer. If there are no consumers are waiting to receive the element, transfer element method will be stored in a queue tail node, and wait until the element is consumer spending and we have to return. The key code transfer method is as follows:

Node pred = tryAppend(s, haveData);
return awaitMatch(s, pred, e, (how == TIMED), nanos);

The first line of code is attempting to store the current node as the element s tail node. The second line is to spin-wait CPU consumer spending elements. Because the spin will consume CPU, so after a certain number of spins using Thread.yield () method to pause the currently executing thread and other threads to execute.

tryTransfer method. Whether it is used to test the element can be passed under the producer directly to consumers. If the consumer is not waiting to receive elements, false is returned. And transfer method difference is tryTransfer method regardless of whether the consumer is received, the method returns immediately. The transfer method is to wait until consumer spending and we have to return.

For tryTransfer with a time limit (E e, long timeout, TimeUnit unit) method, it is trying to producers element passed directly to consumers, but without the element of consumer spending is waiting for a specified time and then return if the consumer has not timeout element, it returns false, if the consumption of the elements within the timeout, it returns true.

LinkedBlockingDeque is a linked list structure consisting of a two-way blocking queue. The so-called two-way means you can queue insertion and removal of elements from both ends of the queue. Deque because more than one operation queue entry, when multiple threads at the same time into the team, it halved the competition. Compared to other blocking queue, LinkedBlockingDeque with more addFirst, addLast, offerFirst, offerLast, peekFirst, peekLast the like, to the end of a word First method for insert obtain (PEEK) or removes the first element deque. Last end of a word to a method for insert or remove the last acquisition element deque. Further insertion method is equivalent to add addLast, remove removal method is equivalent to removeFirst. But take method was equivalent to takeFirst, I do not know Jdk the bug, use or method with First and Last suffix clearer.

When initialization LinkedBlockingDeque capacity may be provided to prevent their expansion transition. In addition two-way blocking queue can be used in "work stealing" mode.

3. The principle of blocking queue

If the queue is empty, consumers will have to wait, when producers add elements, consumers are aware of how the current queue has elements of it? If you were to design a blocking queue How would you design, so that producers and consumers can communicate efficiently it? Let's take a look at how the JDK is achieved.

Using the notification mode to achieve. The so-called notification mode, that is, when the producers to full queue will block live Producer elements are added, when consumer spending elements of a queue, the queue will inform producers currently available. JDK found by viewing the source code used ArrayBlockingQueue Condition to achieve, as follows:

private final Condition notFull;
private final Condition notEmpty;

public ArrayBlockingQueue(int capacity, boolean fair) { //省略其他代码 notEmpty = lock.newCondition(); notFull = lock.newCondition(); } public void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); insert(e); } finally { lock.unlock(); } } public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == 0) notEmpty.await(); return extract(); } finally { lock.unlock(); } } private void insert(E x) { items[putIndex] = x; putIndex = inc(putIndex); ++count; notEmpty.signal(); } 

When we insert an element into the queue, if the queue is not available, primarily by blocking producers LockSupport.park (this); implemented

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) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); } 

Continue into the source code, call setBlocker found to be blocked under the first saved thread, and then call unsafe.park blocks the current thread.

public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park(false, 0L); setBlocker(t, null); } 

unsafe.park is a native method, as follows:

public native void park(boolean isAbsolute, long time); 

park this method blocks the current thread, when only one kind of the following four conditions occurs, the method will return.

  • Corresponding to park or unpark has been performed during execution. Note: refers to unpark has been executed before execution, and then re-execute the park.
  • The thread is interrupted.
  • If the parameter is not zero in time, waiting for a specified number of milliseconds.
  • When anomalies occur. These anomalies can not be determined in advance.

We continue to look at how the JVM is to achieve a method of park, park use different ways on different operating systems, it is under linux using a systems approach pthread_cond_wait achieve. Os JVM implementation code in the source directory src / os / linux / vm / os_linux.cpp :: PlatformEvent :: park in the method, as follows:

void os::PlatformEvent::park() {      
             int v ;
         for (;;) {
        v = _Event ;
         if (Atomic::cmpxchg (v-1, &_Event, v) == v) break ; } guarantee (v >= 0, "invariant") ; if (v == 0) { // Do this the hard way by blocking ... int status = pthread_mutex_lock(_mutex); assert_status(status == 0, status, "mutex_lock"); guarantee (_nParked == 0, "invariant") ; ++ _nParked ; while (_Event < 0) { status = pthread_cond_wait(_cond, _mutex); // for some reason, under 2.7 lwp_cond_wait() may return ETIME ... // Treat this the same as if the wait was interrupted if (status == ETIME) { status = EINTR; } assert_status(status == 0 || status == EINTR, status, "cond_wait"); } -- _nParked ; // In theory we could move the ST of 0 into _Event past the unlock(), // but then we'd need a MEMBAR after the ST. _Event = 0 ; status = pthread_mutex_unlock(_mutex); assert_status(status == 0, status, "mutex_unlock"); } guarantee (_Event >= 0, "invariant") ; } } 

pthread_cond_wait is a multi-threaded condition variable functions, cond is the condition of the abbreviation, the literal meaning can be understood as the thread is waiting for a condition occurs, the condition is a global variable. This method receives two parameters, a shared variable _cond, a mutex _mutex. The method of using the unpark pthread_cond_signal achieved under linux. park in the windows is to use WaitForSingleObject implemented.

When the queue is full, the producers blocking queue to insert an element, producer thread will enter WAITING (parking) state. We can use jstack dump blocked thread producers see this:

"main" prio=5 tid=0x00007fc83c000000 nid=0x10164e000 waiting on condition [0x000000010164d000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x0000000140559fe8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043) at java.util.concurrent.ArrayBlockingQueue.put(ArrayBlockingQueue.java:324) at blockingqueue.ArrayBlockingQueueTest.main(ArrayBlockingQueueTest.java:11) 

Reference Links: http://www.infoq.com/cn/articles/java-blocking-queue



Guess you like

Origin www.cnblogs.com/renjiaqi/p/11351570.html