diff --git a/src/main/java/test/SomeQueue.java b/src/main/java/test/SomeQueue.java index adecf86..b0a4d42 100644 --- a/src/main/java/test/SomeQueue.java +++ b/src/main/java/test/SomeQueue.java @@ -35,8 +35,6 @@ package test; -import sun.misc.Unsafe; - import java.lang.reflect.Field; import java.util.AbstractQueue; import java.util.ArrayList; @@ -47,909 +45,818 @@ import java.util.Spliterator; import java.util.Spliterators; import java.util.function.Consumer; +import sun.misc.Unsafe; /** - * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. - * This queue orders elements FIFO (first-in-first-out). - * The head of the queue is that element that has been on the - * queue the longest time. - * The tail of the queue is that element that has been on the - * queue the shortest time. New elements - * are inserted at the tail of the queue, and the queue retrieval - * operations obtain elements at the head of the queue. - * A {@code SomeQueue} is an appropriate choice when - * many threads will share access to a common collection. - * Like most other concurrent collection implementations, this class - * does not permit the use of {@code null} elements. - * - *

This implementation employs an efficient non-blocking - * algorithm based on one described in Simple, - * Fast, and Practical Non-Blocking and Blocking Concurrent Queue - * Algorithms by Maged M. Michael and Michael L. Scott. - * - *

Iterators are weakly consistent, returning elements - * reflecting the state of the queue at some point at or since the - * creation of the iterator. They do not throw {@link - * java.util.ConcurrentModificationException}, and may proceed concurrently - * with other operations. Elements contained in the queue since the creation - * of the iterator will be returned exactly once. - * - *

Beware that, unlike in most collections, the {@code size} method - * is NOT a constant-time operation. Because of the - * asynchronous nature of these queues, determining the current number - * of elements requires a traversal of the elements, and so may report - * inaccurate results if this collection is modified during traversal. - * Additionally, the bulk operations {@code addAll}, - * {@code removeAll}, {@code retainAll}, {@code containsAll}, - * {@code equals}, and {@code toArray} are not guaranteed - * to be performed atomically. For example, an iterator operating - * concurrently with an {@code addAll} operation might view only some - * of the added elements. - * - *

This class and its iterator implement all of the optional - * methods of the {@link Queue} and {@link Iterator} interfaces. - * - *

Memory consistency effects: As with other concurrent - * collections, actions in a thread prior to placing an object into a - * {@code SomeQueue} - * happen-before - * actions subsequent to the access or removal of that element from - * the {@code SomeQueue} in another thread. - * - *

This class is a member of the - * + * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. This queue orders + * elements FIFO (first-in-first-out). The head of the queue is that element that has been + * on the queue the longest time. The tail of the queue is that element that has been on + * the queue the shortest time. New elements are inserted at the tail of the queue, and the queue + * retrieval operations obtain elements at the head of the queue. A {@code SomeQueue} is an + * appropriate choice when many threads will share access to a common collection. Like most other + * concurrent collection implementations, this class does not permit the use of {@code null} + * elements. + * + *

This implementation employs an efficient non-blocking algorithm based on one + * described in Simple, Fast, and + * Practical Non-Blocking and Blocking Concurrent Queue Algorithms by Maged M. Michael and + * Michael L. Scott. + * + *

Iterators are weakly consistent, returning elements reflecting the state of the queue + * at some point at or since the creation of the iterator. They do not throw {@link + * java.util.ConcurrentModificationException}, and may proceed concurrently with other operations. + * Elements contained in the queue since the creation of the iterator will be returned exactly once. + * + *

Beware that, unlike in most collections, the {@code size} method is NOT a + * constant-time operation. Because of the asynchronous nature of these queues, determining the + * current number of elements requires a traversal of the elements, and so may report inaccurate + * results if this collection is modified during traversal. Additionally, the bulk operations {@code + * addAll}, {@code removeAll}, {@code retainAll}, {@code containsAll}, {@code equals}, and {@code + * toArray} are not guaranteed to be performed atomically. For example, an iterator + * operating concurrently with an {@code addAll} operation might view only some of the added + * elements. + * + *

This class and its iterator implement all of the optional methods of the {@link + * Queue} and {@link Iterator} interfaces. + * + *

Memory consistency effects: As with other concurrent collections, actions in a thread prior to + * placing an object into a {@code SomeQueue} happen-before actions subsequent to the + * access or removal of that element from the {@code SomeQueue} in another thread. + * + *

This class is a member of the * Java Collections Framework. * * @since 1.5 * @author Doug Lea * @param the type of elements held in this collection */ -public class SomeQueue extends AbstractQueue - implements Queue, java.io.Serializable { - private static final long serialVersionUID = 196745693267521676L; - - /* - * This is a modification of the Michael & Scott algorithm, - * adapted for a garbage-collected environment, with support for - * interior node deletion (to support remove(Object)). For - * explanation, read the paper. - * - * Note that like most non-blocking algorithms in this package, - * this implementation relies on the fact that in garbage - * collected systems, there is no possibility of ABA problems due - * to recycled nodes, so there is no need to use "counted - * pointers" or related techniques seen in versions used in - * non-GC'ed settings. - * - * The fundamental invariants are: - * - There is exactly one (last) Node with a null next reference, - * which is CASed when enqueueing. This last Node can be - * reached in O(1) time from tail, but tail is merely an - * optimization - it can always be reached in O(N) time from - * head as well. - * - The elements contained in the queue are the non-null items in - * Nodes that are reachable from head. CASing the item - * reference of a Node to null atomically removes it from the - * queue. Reachability of all elements from head must remain - * true even in the case of concurrent modifications that cause - * head to advance. A dequeued Node may remain in use - * indefinitely due to creation of an Iterator or simply a - * poll() that has lost its time slice. - * - * The above might appear to imply that all Nodes are GC-reachable - * from a predecessor dequeued Node. That would cause two problems: - * - allow a rogue Iterator to cause unbounded memory retention - * - cause cross-generational linking of old Nodes to new Nodes if - * a Node was tenured while live, which generational GCs have a - * hard time dealing with, causing repeated major collections. - * However, only non-deleted Nodes need to be reachable from - * dequeued Nodes, and reachability does not necessarily have to - * be of the kind understood by the GC. We use the trick of - * linking a Node that has just been dequeued to itself. Such a - * self-link implicitly means to advance to head. - * - * Both head and tail are permitted to lag. In fact, failing to - * update them every time one could is a significant optimization - * (fewer CASes). As with LinkedTransferQueue (see the internal - * documentation for that class), we use a slack threshold of two; - * that is, we update head/tail when the current pointer appears - * to be two or more steps away from the first/last node. - * - * Since head and tail are updated concurrently and independently, - * it is possible for tail to lag behind head (why not)? - * - * CASing a Node's item reference to null atomically removes the - * element from the queue. Iterators skip over Nodes with null - * items. Prior implementations of this class had a race between - * poll() and remove(Object) where the same element would appear - * to be successfully removed by two concurrent operations. The - * method remove(Object) also lazily unlinks deleted Nodes, but - * this is merely an optimization. - * - * When constructing a Node (before enqueuing it) we avoid paying - * for a volatile write to item by using Unsafe.putObject instead - * of a normal write. This allows the cost of enqueue to be - * "one-and-a-half" CASes. - * - * Both head and tail may or may not point to a Node with a - * non-null item. If the queue is empty, all items must of course - * be null. Upon creation, both head and tail refer to a dummy - * Node with null item. Both head and tail are only updated using - * CAS, so they never regress, although again this is merely an - * optimization. - */ - - private static class Node { - volatile E item; - volatile Node next; - - /** - * Constructs a new node. Uses relaxed write because item can - * only be seen after publication via casNext. - */ - Node(E item) { - UNSAFE.putObject(this, itemOffset, item); - } - - boolean casItem(E cmp, E val) { - return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); - } - - void lazySetNext(Node val) { - UNSAFE.putOrderedObject(this, nextOffset, val); - } - - boolean casNext(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); - } - - // Unsafe mechanics - - private static final sun.misc.Unsafe UNSAFE; - private static final long itemOffset; - private static final long nextOffset; - - static { - try { - final Field field = Unsafe.class.getDeclaredField("theUnsafe"); - field.setAccessible(true); - UNSAFE = (Unsafe) field.get(null); - Class k = Node.class; - itemOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("item")); - nextOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("next")); - } catch (Exception e) { - throw new Error(e); - } - } - } +public class SomeQueue extends AbstractQueue implements Queue, java.io.Serializable { + private static final long serialVersionUID = 196745693267521676L; + + /* + * This is a modification of the Michael & Scott algorithm, + * adapted for a garbage-collected environment, with support for + * interior node deletion (to support remove(Object)). For + * explanation, read the paper. + * + * Note that like most non-blocking algorithms in this package, + * this implementation relies on the fact that in garbage + * collected systems, there is no possibility of ABA problems due + * to recycled nodes, so there is no need to use "counted + * pointers" or related techniques seen in versions used in + * non-GC'ed settings. + * + * The fundamental invariants are: + * - There is exactly one (last) Node with a null next reference, + * which is CASed when enqueueing. This last Node can be + * reached in O(1) time from tail, but tail is merely an + * optimization - it can always be reached in O(N) time from + * head as well. + * - The elements contained in the queue are the non-null items in + * Nodes that are reachable from head. CASing the item + * reference of a Node to null atomically removes it from the + * queue. Reachability of all elements from head must remain + * true even in the case of concurrent modifications that cause + * head to advance. A dequeued Node may remain in use + * indefinitely due to creation of an Iterator or simply a + * poll() that has lost its time slice. + * + * The above might appear to imply that all Nodes are GC-reachable + * from a predecessor dequeued Node. That would cause two problems: + * - allow a rogue Iterator to cause unbounded memory retention + * - cause cross-generational linking of old Nodes to new Nodes if + * a Node was tenured while live, which generational GCs have a + * hard time dealing with, causing repeated major collections. + * However, only non-deleted Nodes need to be reachable from + * dequeued Nodes, and reachability does not necessarily have to + * be of the kind understood by the GC. We use the trick of + * linking a Node that has just been dequeued to itself. Such a + * self-link implicitly means to advance to head. + * + * Both head and tail are permitted to lag. In fact, failing to + * update them every time one could is a significant optimization + * (fewer CASes). As with LinkedTransferQueue (see the internal + * documentation for that class), we use a slack threshold of two; + * that is, we update head/tail when the current pointer appears + * to be two or more steps away from the first/last node. + * + * Since head and tail are updated concurrently and independently, + * it is possible for tail to lag behind head (why not)? + * + * CASing a Node's item reference to null atomically removes the + * element from the queue. Iterators skip over Nodes with null + * items. Prior implementations of this class had a race between + * poll() and remove(Object) where the same element would appear + * to be successfully removed by two concurrent operations. The + * method remove(Object) also lazily unlinks deleted Nodes, but + * this is merely an optimization. + * + * When constructing a Node (before enqueuing it) we avoid paying + * for a volatile write to item by using Unsafe.putObject instead + * of a normal write. This allows the cost of enqueue to be + * "one-and-a-half" CASes. + * + * Both head and tail may or may not point to a Node with a + * non-null item. If the queue is empty, all items must of course + * be null. Upon creation, both head and tail refer to a dummy + * Node with null item. Both head and tail are only updated using + * CAS, so they never regress, although again this is merely an + * optimization. + */ + + private static class Node { + volatile E item; + volatile Node next; /** - * A node from which the first live (non-deleted) node (if any) - * can be reached in O(1) time. - * Invariants: - * - all live nodes are reachable from head via succ() - * - head != null - * - (tmp = head).next != tmp || tmp != head - * Non-invariants: - * - head.item may or may not be null. - * - it is permitted for tail to lag behind head, that is, for tail - * to not be reachable from head! + * Constructs a new node. Uses relaxed write because item can only be seen after publication via + * casNext. */ - private transient volatile Node head; + Node(E item) { + UNSAFE.putObject(this, itemOffset, item); + } - /** - * A node from which the last node on list (that is, the unique - * node with node.next == null) can be reached in O(1) time. - * Invariants: - * - the last node is always reachable from tail via succ() - * - tail != null - * Non-invariants: - * - tail.item may or may not be null. - * - it is permitted for tail to lag behind head, that is, for tail - * to not be reachable from head! - * - tail.next may or may not be self-pointing to tail. - */ - private transient volatile Node tail; + boolean casItem(E cmp, E val) { + return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); + } - /** - * Creates a {@code SomeQueue} that is initially empty. - */ - public SomeQueue() { - head = tail = new Node(null); + void lazySetNext(Node val) { + UNSAFE.putOrderedObject(this, nextOffset, val); } - /** - * Creates a {@code SomeQueue} - * initially containing the elements of the given collection, - * added in traversal order of the collection's iterator. - * - * @param c the collection of elements to initially contain - * @throws NullPointerException if the specified collection or any - * of its elements are null - */ - public SomeQueue(Collection c) { - Node h = null, t = null; - for (E e : c) { - checkNotNull(e); - Node newNode = new Node(e); - if (h == null) - h = t = newNode; - else { - t.lazySetNext(newNode); - t = newNode; - } - } - if (h == null) - h = t = new Node(null); - head = h; - tail = t; + boolean casNext(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); } - // Have to override just to update the javadoc + // Unsafe mechanics - /** - * Inserts the specified element at the tail of this queue. - * As the queue is unbounded, this method will never throw - * {@link IllegalStateException} or return {@code false}. - * - * @return {@code true} (as specified by {@link Collection#add}) - * @throws NullPointerException if the specified element is null - */ - public boolean add(E e) { - return offer(e); - } + private static final sun.misc.Unsafe UNSAFE; + private static final long itemOffset; + private static final long nextOffset; - /** - * Tries to CAS head to p. If successful, repoint old head to itself - * as sentinel for succ(), below. - */ - final void updateHead(Node h, Node p) { - if (h != p && casHead(h, p)) - h.lazySetNext(h); + static { + try { + final Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + UNSAFE = (Unsafe) field.get(null); + Class k = Node.class; + itemOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("item")); + nextOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("next")); + } catch (Exception e) { + throw new Error(e); + } } - - /** - * Returns the successor of p, or the head node if p.next has been - * linked to self, which will only be true if traversing with a - * stale pointer that is now off the list. - */ - final Node succ(Node p) { - Node next = p.next; - return (p == next) ? head : next; + } + + /** + * A node from which the first live (non-deleted) node (if any) can be reached in O(1) time. + * Invariants: - all live nodes are reachable from head via succ() - head != null - (tmp = + * head).next != tmp || tmp != head Non-invariants: - head.item may or may not be null. - it is + * permitted for tail to lag behind head, that is, for tail to not be reachable from head! + */ + private transient volatile Node head; + + /** + * A node from which the last node on list (that is, the unique node with node.next == null) can + * be reached in O(1) time. Invariants: - the last node is always reachable from tail via succ() - + * tail != null Non-invariants: - tail.item may or may not be null. - it is permitted for tail to + * lag behind head, that is, for tail to not be reachable from head! - tail.next may or may not be + * self-pointing to tail. + */ + private transient volatile Node tail; + + /** Creates a {@code SomeQueue} that is initially empty. */ + public SomeQueue() { + head = tail = new Node(null); + } + + /** + * Creates a {@code SomeQueue} initially containing the elements of the given collection, added in + * traversal order of the collection's iterator. + * + * @param c the collection of elements to initially contain + * @throws NullPointerException if the specified collection or any of its elements are null + */ + public SomeQueue(Collection c) { + Node h = null, t = null; + for (E e : c) { + checkNotNull(e); + Node newNode = new Node(e); + if (h == null) h = t = newNode; + else { + t.lazySetNext(newNode); + t = newNode; + } } - - /** - * Inserts the specified element at the tail of this queue. - * As the queue is unbounded, this method will never return {@code false}. - * - * @return {@code true} (as specified by {@link Queue#offer}) - * @throws NullPointerException if the specified element is null - */ - public boolean offer(E e) { - checkNotNull(e); - final Node newNode = new Node(e); - - for (Node t = tail, p = t;;) { - Node q = p.next; - if (q == null) { - // p is last node - if (p.casNext(null, newNode)) { - // Successful CAS is the linearization point - // for e to become an element of this queue, - // and for newNode to become "live". - if (p != t) // hop two nodes at a time - casTail(t, newNode); // Failure is OK. - return true; - } - // Lost CAS race to another thread; re-read next - } - else if (p == q) - // We have fallen off list. If tail is unchanged, it - // will also be off-list, in which case we need to - // jump to head, from which all live nodes are always - // reachable. Else the new tail is a better bet. - p = (t != (t = tail)) ? t : head; - else - // Check for tail updates after two hops. - p = (p != t && t != (t = tail)) ? t : q; + if (h == null) h = t = new Node(null); + head = h; + tail = t; + } + + // Have to override just to update the javadoc + + /** + * Inserts the specified element at the tail of this queue. As the queue is unbounded, this method + * will never throw {@link IllegalStateException} or return {@code false}. + * + * @return {@code true} (as specified by {@link Collection#add}) + * @throws NullPointerException if the specified element is null + */ + @Override + public boolean add(E e) { + return offer(e); + } + + /** + * Tries to CAS head to p. If successful, repoint old head to itself as sentinel for succ(), + * below. + */ + final void updateHead(Node h, Node p) { + if (h != p && casHead(h, p)) h.lazySetNext(h); + } + + /** + * Returns the successor of p, or the head node if p.next has been linked to self, which will only + * be true if traversing with a stale pointer that is now off the list. + */ + final Node succ(Node p) { + Node next = p.next; + return (p == next) ? head : next; + } + + /** + * Inserts the specified element at the tail of this queue. As the queue is unbounded, this method + * will never return {@code false}. + * + * @return {@code true} (as specified by {@link Queue#offer}) + * @throws NullPointerException if the specified element is null + */ + @Override + public boolean offer(E e) { + checkNotNull(e); + final Node newNode = new Node(e); + + for (Node t = tail, p = t; ; ) { + Node q = p.next; + if (q == null) { + // p is last node + if (p.casNext(null, newNode)) { + // Successful CAS is the linearization point + // for e to become an element of this queue, + // and for newNode to become "live". + if (p != t) // hop two nodes at a time + casTail(t, newNode); // Failure is OK. + return true; } + // Lost CAS race to another thread; re-read next + } else if (p == q) + // We have fallen off list. If tail is unchanged, it + // will also be off-list, in which case we need to + // jump to head, from which all live nodes are always + // reachable. Else the new tail is a better bet. + p = (t != (t = tail)) ? t : head; + else + // Check for tail updates after two hops. + p = (p != t && t != (t = tail)) ? t : q; } - - public E poll() { - restartFromHead: - for (;;) { - for (Node h = head, p = h, q;;) { - E item = p.item; - - if (item != null && p.casItem(item, null)) { - // Successful CAS is the linearization point - // for item to be removed from this queue. - if (p != h) // hop two nodes at a time - updateHead(h, ((q = p.next) != null) ? q : p); - return item; - } - else if ((q = p.next) == null) { - updateHead(h, p); - return null; - } - else if (p == q) - continue restartFromHead; - else - p = q; - } - } + } + + @Override + public E poll() { + restartFromHead: + for (; ; ) { + for (Node h = head, p = h, q; ; ) { + E item = p.item; + + if (item != null && p.casItem(item, null)) { + // Successful CAS is the linearization point + // for item to be removed from this queue. + if (p != h) // hop two nodes at a time + updateHead(h, ((q = p.next) != null) ? q : p); + return item; + } else if ((q = p.next) == null) { + updateHead(h, p); + return null; + } else if (p == q) continue restartFromHead; + else p = q; + } } - - public E peek() { - restartFromHead: - for (;;) { - for (Node h = head, p = h, q;;) { - E item = p.item; - if (item != null || (q = p.next) == null) { - updateHead(h, p); - return item; - } - else if (p == q) - continue restartFromHead; - else - p = q; - } - } + } + + @Override + public E peek() { + restartFromHead: + for (; ; ) { + for (Node h = head, p = h, q; ; ) { + E item = p.item; + if (item != null || (q = p.next) == null) { + updateHead(h, p); + return item; + } else if (p == q) continue restartFromHead; + else p = q; + } } - - /** - * Returns the first live (non-deleted) node on list, or null if none. - * This is yet another variant of poll/peek; here returning the - * first node, not element. We could make peek() a wrapper around - * first(), but that would cost an extra volatile read of item, - * and the need to add a retry loop to deal with the possibility - * of losing a race to a concurrent poll(). - */ - Node first() { - restartFromHead: - for (;;) { - for (Node h = head, p = h, q;;) { - boolean hasItem = (p.item != null); - if (hasItem || (q = p.next) == null) { - updateHead(h, p); - return hasItem ? p : null; - } - else if (p == q) - continue restartFromHead; - else - p = q; - } - } + } + + /** + * Returns the first live (non-deleted) node on list, or null if none. This is yet another variant + * of poll/peek; here returning the first node, not element. We could make peek() a wrapper around + * first(), but that would cost an extra volatile read of item, and the need to add a retry loop + * to deal with the possibility of losing a race to a concurrent poll(). + */ + Node first() { + restartFromHead: + for (; ; ) { + for (Node h = head, p = h, q; ; ) { + boolean hasItem = (p.item != null); + if (hasItem || (q = p.next) == null) { + updateHead(h, p); + return hasItem ? p : null; + } else if (p == q) continue restartFromHead; + else p = q; + } } - - /** - * Returns {@code true} if this queue contains no elements. - * - * @return {@code true} if this queue contains no elements - */ - public boolean isEmpty() { - return first() == null; + } + + /** + * Returns {@code true} if this queue contains no elements. + * + * @return {@code true} if this queue contains no elements + */ + @Override + public boolean isEmpty() { + return first() == null; + } + + /** + * Returns the number of elements in this queue. If this queue contains more than {@code + * Integer.MAX_VALUE} elements, returns {@code Integer.MAX_VALUE}. + * + *

Beware that, unlike in most collections, this method is NOT a constant-time + * operation. Because of the asynchronous nature of these queues, determining the current number + * of elements requires an O(n) traversal. Additionally, if elements are added or removed during + * execution of this method, the returned result may be inaccurate. Thus, this method is typically + * not very useful in concurrent applications. + * + * @return the number of elements in this queue + */ + @Override + public int size() { + int count = 0; + for (Node p = first(); p != null; p = succ(p)) + if (p.item != null) + // Collection.size() spec says to max out + if (++count == Integer.MAX_VALUE) break; + return count; + } + + /** + * Returns {@code true} if this queue contains the specified element. More formally, returns + * {@code true} if and only if this queue contains at least one element {@code e} such that {@code + * o.equals(e)}. + * + * @param o object to be checked for containment in this queue + * @return {@code true} if this queue contains the specified element + */ + @Override + public boolean contains(Object o) { + if (o == null) return false; + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null && o.equals(item)) return true; } + return false; + } + + /** + * Removes a single instance of the specified element from this queue, if it is present. More + * formally, removes an element {@code e} such that {@code o.equals(e)}, if this queue contains + * one or more such elements. Returns {@code true} if this queue contained the specified element + * (or equivalently, if this queue changed as a result of the call). + * + * @param o element to be removed from this queue, if present + * @return {@code true} if this queue changed as a result of the call + */ + @Override + public boolean remove(Object o) { + if (o != null) { + Node next, pred = null; + for (Node p = first(); p != null; pred = p, p = next) { + boolean removed = false; + E item = p.item; + if (item != null) { + if (!o.equals(item)) { + next = succ(p); + continue; + } + removed = p.casItem(item, null); + } - /** - * Returns the number of elements in this queue. If this queue - * contains more than {@code Integer.MAX_VALUE} elements, returns - * {@code Integer.MAX_VALUE}. - * - *

Beware that, unlike in most collections, this method is - * NOT a constant-time operation. Because of the - * asynchronous nature of these queues, determining the current - * number of elements requires an O(n) traversal. - * Additionally, if elements are added or removed during execution - * of this method, the returned result may be inaccurate. Thus, - * this method is typically not very useful in concurrent - * applications. - * - * @return the number of elements in this queue - */ - public int size() { - int count = 0; - for (Node p = first(); p != null; p = succ(p)) - if (p.item != null) - // Collection.size() spec says to max out - if (++count == Integer.MAX_VALUE) - break; - return count; + next = succ(p); + if (pred != null && next != null) // unlink + pred.casNext(p, next); + if (removed) return true; + } } - - /** - * Returns {@code true} if this queue contains the specified element. - * More formally, returns {@code true} if and only if this queue contains - * at least one element {@code e} such that {@code o.equals(e)}. - * - * @param o object to be checked for containment in this queue - * @return {@code true} if this queue contains the specified element - */ - public boolean contains(Object o) { - if (o == null) return false; - for (Node p = first(); p != null; p = succ(p)) { - E item = p.item; - if (item != null && o.equals(item)) - return true; - } - return false; + return false; + } + + /** + * Appends all of the elements in the specified collection to the end of this queue, in the order + * that they are returned by the specified collection's iterator. Attempts to {@code addAll} of a + * queue to itself result in {@code IllegalArgumentException}. + * + * @param c the elements to be inserted into this queue + * @return {@code true} if this queue changed as a result of the call + * @throws NullPointerException if the specified collection or any of its elements are null + * @throws IllegalArgumentException if the collection is this queue + */ + @Override + public boolean addAll(Collection c) { + if (c == this) + // As historically specified in AbstractQueue#addAll + throw new IllegalArgumentException(); + + // Copy c into a private chain of Nodes + Node beginningOfTheEnd = null, last = null; + for (E e : c) { + checkNotNull(e); + Node newNode = new Node(e); + if (beginningOfTheEnd == null) beginningOfTheEnd = last = newNode; + else { + last.lazySetNext(newNode); + last = newNode; + } } - - /** - * Removes a single instance of the specified element from this queue, - * if it is present. More formally, removes an element {@code e} such - * that {@code o.equals(e)}, if this queue contains one or more such - * elements. - * Returns {@code true} if this queue contained the specified element - * (or equivalently, if this queue changed as a result of the call). - * - * @param o element to be removed from this queue, if present - * @return {@code true} if this queue changed as a result of the call - */ - public boolean remove(Object o) { - if (o != null) { - Node next, pred = null; - for (Node p = first(); p != null; pred = p, p = next) { - boolean removed = false; - E item = p.item; - if (item != null) { - if (!o.equals(item)) { - next = succ(p); - continue; - } - removed = p.casItem(item, null); - } - - next = succ(p); - if (pred != null && next != null) // unlink - pred.casNext(p, next); - if (removed) - return true; - } + if (beginningOfTheEnd == null) return false; + + // Atomically append the chain at the tail of this collection + for (Node t = tail, p = t; ; ) { + Node q = p.next; + if (q == null) { + // p is last node + if (p.casNext(null, beginningOfTheEnd)) { + // Successful CAS is the linearization point + // for all elements to be added to this queue. + if (!casTail(t, last)) { + // Try a little harder to update tail, + // since we may be adding many elements. + t = tail; + if (last.next == null) casTail(t, last); + } + return true; } - return false; + // Lost CAS race to another thread; re-read next + } else if (p == q) + // We have fallen off list. If tail is unchanged, it + // will also be off-list, in which case we need to + // jump to head, from which all live nodes are always + // reachable. Else the new tail is a better bet. + p = (t != (t = tail)) ? t : head; + else + // Check for tail updates after two hops. + p = (p != t && t != (t = tail)) ? t : q; } - - /** - * Appends all of the elements in the specified collection to the end of - * this queue, in the order that they are returned by the specified - * collection's iterator. Attempts to {@code addAll} of a queue to - * itself result in {@code IllegalArgumentException}. - * - * @param c the elements to be inserted into this queue - * @return {@code true} if this queue changed as a result of the call - * @throws NullPointerException if the specified collection or any - * of its elements are null - * @throws IllegalArgumentException if the collection is this queue - */ - public boolean addAll(Collection c) { - if (c == this) - // As historically specified in AbstractQueue#addAll - throw new IllegalArgumentException(); - - // Copy c into a private chain of Nodes - Node beginningOfTheEnd = null, last = null; - for (E e : c) { - checkNotNull(e); - Node newNode = new Node(e); - if (beginningOfTheEnd == null) - beginningOfTheEnd = last = newNode; - else { - last.lazySetNext(newNode); - last = newNode; - } - } - if (beginningOfTheEnd == null) - return false; - - // Atomically append the chain at the tail of this collection - for (Node t = tail, p = t;;) { - Node q = p.next; - if (q == null) { - // p is last node - if (p.casNext(null, beginningOfTheEnd)) { - // Successful CAS is the linearization point - // for all elements to be added to this queue. - if (!casTail(t, last)) { - // Try a little harder to update tail, - // since we may be adding many elements. - t = tail; - if (last.next == null) - casTail(t, last); - } - return true; - } - // Lost CAS race to another thread; re-read next - } - else if (p == q) - // We have fallen off list. If tail is unchanged, it - // will also be off-list, in which case we need to - // jump to head, from which all live nodes are always - // reachable. Else the new tail is a better bet. - p = (t != (t = tail)) ? t : head; - else - // Check for tail updates after two hops. - p = (p != t && t != (t = tail)) ? t : q; - } + } + + /** + * Returns an array containing all of the elements in this queue, in proper sequence. + * + *

The returned array will be "safe" in that no references to it are maintained by this queue. + * (In other words, this method must allocate a new array). The caller is thus free to modify the + * returned array. + * + *

This method acts as bridge between array-based and collection-based APIs. + * + * @return an array containing all of the elements in this queue + */ + @Override + public Object[] toArray() { + // Use ArrayList to deal with resizing. + ArrayList al = new ArrayList(); + for (Node p = first(); p != null; p = succ(p)) { + E item = p.item; + if (item != null) al.add(item); } - - /** - * Returns an array containing all of the elements in this queue, in - * proper sequence. - * - *

The returned array will be "safe" in that no references to it are - * maintained by this queue. (In other words, this method must allocate - * a new array). The caller is thus free to modify the returned array. - * - *

This method acts as bridge between array-based and collection-based - * APIs. - * - * @return an array containing all of the elements in this queue - */ - public Object[] toArray() { - // Use ArrayList to deal with resizing. - ArrayList al = new ArrayList(); - for (Node p = first(); p != null; p = succ(p)) { - E item = p.item; - if (item != null) - al.add(item); - } - return al.toArray(); + return al.toArray(); + } + + /** + * Returns an array containing all of the elements in this queue, in proper sequence; the runtime + * type of the returned array is that of the specified array. If the queue fits in the specified + * array, it is returned therein. Otherwise, a new array is allocated with the runtime type of the + * specified array and the size of this queue. + * + *

If this queue fits in the specified array with room to spare (i.e., the array has more + * elements than this queue), the element in the array immediately following the end of the queue + * is set to {@code null}. + * + *

Like the {@link #toArray()} method, this method acts as bridge between array-based and + * collection-based APIs. Further, this method allows precise control over the runtime type of the + * output array, and may, under certain circumstances, be used to save allocation costs. + * + *

Suppose {@code x} is a queue known to contain only strings. The following code can be used + * to dump the queue into a newly allocated array of {@code String}: + * + *

 {@code String[] y = x.toArray(new String[0]);}
+ * + * Note that {@code toArray(new Object[0])} is identical in function to {@code toArray()}. + * + * @param a the array into which the elements of the queue are to be stored, if it is big enough; + * otherwise, a new array of the same runtime type is allocated for this purpose + * @return an array containing all of the elements in this queue + * @throws ArrayStoreException if the runtime type of the specified array is not a supertype of + * the runtime type of every element in this queue + * @throws NullPointerException if the specified array is null + */ + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + // try to use sent-in array + int k = 0; + Node p; + for (p = first(); p != null && k < a.length; p = succ(p)) { + E item = p.item; + if (item != null) a[k++] = (T) item; } - - /** - * Returns an array containing all of the elements in this queue, in - * proper sequence; the runtime type of the returned array is that of - * the specified array. If the queue fits in the specified array, it - * is returned therein. Otherwise, a new array is allocated with the - * runtime type of the specified array and the size of this queue. - * - *

If this queue fits in the specified array with room to spare - * (i.e., the array has more elements than this queue), the element in - * the array immediately following the end of the queue is set to - * {@code null}. - * - *

Like the {@link #toArray()} method, this method acts as bridge between - * array-based and collection-based APIs. Further, this method allows - * precise control over the runtime type of the output array, and may, - * under certain circumstances, be used to save allocation costs. - * - *

Suppose {@code x} is a queue known to contain only strings. - * The following code can be used to dump the queue into a newly - * allocated array of {@code String}: - * - *

 {@code String[] y = x.toArray(new String[0]);}
- * - * Note that {@code toArray(new Object[0])} is identical in function to - * {@code toArray()}. - * - * @param a the array into which the elements of the queue are to - * be stored, if it is big enough; otherwise, a new array of the - * same runtime type is allocated for this purpose - * @return an array containing all of the elements in this queue - * @throws ArrayStoreException if the runtime type of the specified array - * is not a supertype of the runtime type of every element in - * this queue - * @throws NullPointerException if the specified array is null - */ - @SuppressWarnings("unchecked") - public T[] toArray(T[] a) { - // try to use sent-in array - int k = 0; - Node p; - for (p = first(); p != null && k < a.length; p = succ(p)) { - E item = p.item; - if (item != null) - a[k++] = (T)item; - } - if (p == null) { - if (k < a.length) - a[k] = null; - return a; - } - - // If won't fit, use ArrayList version - ArrayList al = new ArrayList(); - for (Node q = first(); q != null; q = succ(q)) { - E item = q.item; - if (item != null) - al.add(item); - } - return al.toArray(a); + if (p == null) { + if (k < a.length) a[k] = null; + return a; } - /** - * Returns an iterator over the elements in this queue in proper sequence. - * The elements will be returned in order from first (head) to last (tail). - * - *

The returned iterator is - * weakly consistent. - * - * @return an iterator over the elements in this queue in proper sequence - */ - public Iterator iterator() { - return new Itr(); - } - - private class Itr implements Iterator { - /** - * Next node to return item for. - */ - private Node nextNode; - - /** - * nextItem holds on to item fields because once we claim - * that an element exists in hasNext(), we must return it in - * the following next() call even if it was in the process of - * being removed when hasNext() was called. - */ - private E nextItem; - - /** - * Node of the last returned item, to support remove. - */ - private Node lastRet; - - Itr() { - advance(); - } - - /** - * Moves to next valid node and returns item to return for - * next(), or null if no such. - */ - private E advance() { - lastRet = nextNode; - E x = nextItem; - - Node pred, p; - if (nextNode == null) { - p = first(); - pred = null; - } else { - pred = nextNode; - p = succ(nextNode); - } - - for (;;) { - if (p == null) { - nextNode = null; - nextItem = null; - return x; - } - E item = p.item; - if (item != null) { - nextNode = p; - nextItem = item; - return x; - } else { - // skip over nulls - Node next = succ(p); - if (pred != null && next != null) - pred.casNext(p, next); - p = next; - } - } - } - - public boolean hasNext() { - return nextNode != null; - } - - public E next() { - if (nextNode == null) throw new NoSuchElementException(); - return advance(); - } - - public void remove() { - Node l = lastRet; - if (l == null) throw new IllegalStateException(); - // rely on a future traversal to relink. - l.item = null; - lastRet = null; - } + // If won't fit, use ArrayList version + ArrayList al = new ArrayList(); + for (Node q = first(); q != null; q = succ(q)) { + E item = q.item; + if (item != null) al.add(item); } + return al.toArray(a); + } + + /** + * Returns an iterator over the elements in this queue in proper sequence. The elements will be + * returned in order from first (head) to last (tail). + * + *

The returned iterator is weakly consistent. + * + * @return an iterator over the elements in this queue in proper sequence + */ + @Override + public Iterator iterator() { + return new Itr(); + } + + private class Itr implements Iterator { + /** Next node to return item for. */ + private Node nextNode; /** - * Saves this queue to a stream (that is, serializes it). - * - * @param s the stream - * @throws java.io.IOException if an I/O error occurs - * @serialData All of the elements (each an {@code E}) in - * the proper order, followed by a null + * nextItem holds on to item fields because once we claim that an element exists in hasNext(), + * we must return it in the following next() call even if it was in the process of being removed + * when hasNext() was called. */ - private void writeObject(java.io.ObjectOutputStream s) - throws java.io.IOException { + private E nextItem; - // Write out any hidden stuff - s.defaultWriteObject(); + /** Node of the last returned item, to support remove. */ + private Node lastRet; - // Write out all elements in the proper order. - for (Node p = first(); p != null; p = succ(p)) { - Object item = p.item; - if (item != null) - s.writeObject(item); - } - - // Use trailing null as sentinel - s.writeObject(null); + Itr() { + advance(); } - /** - * Reconstitutes this queue from a stream (that is, deserializes it). - * @param s the stream - * @throws ClassNotFoundException if the class of a serialized object - * could not be found - * @throws java.io.IOException if an I/O error occurs - */ - private void readObject(java.io.ObjectInputStream s) - throws java.io.IOException, ClassNotFoundException { - s.defaultReadObject(); - - // Read in elements until trailing null sentinel found - Node h = null, t = null; - Object item; - while ((item = s.readObject()) != null) { - @SuppressWarnings("unchecked") - Node newNode = new Node((E) item); - if (h == null) - h = t = newNode; - else { - t.lazySetNext(newNode); - t = newNode; - } + /** Moves to next valid node and returns item to return for next(), or null if no such. */ + private E advance() { + lastRet = nextNode; + E x = nextItem; + + Node pred, p; + if (nextNode == null) { + p = first(); + pred = null; + } else { + pred = nextNode; + p = succ(nextNode); + } + + for (; ; ) { + if (p == null) { + nextNode = null; + nextItem = null; + return x; } - if (h == null) - h = t = new Node(null); - head = h; - tail = t; - } - - /** A customized variant of Spliterators.IteratorSpliterator */ - static final class CLQSpliterator implements Spliterator { - static final int MAX_BATCH = 1 << 25; // max batch array size; - final SomeQueue queue; - Node current; // current node; null until initialized - int batch; // batch size for splits - boolean exhausted; // true when no more nodes - CLQSpliterator(SomeQueue queue) { - this.queue = queue; + E item = p.item; + if (item != null) { + nextNode = p; + nextItem = item; + return x; + } else { + // skip over nulls + Node next = succ(p); + if (pred != null && next != null) pred.casNext(p, next); + p = next; } + } + } - public Spliterator trySplit() { - Node p; - final SomeQueue q = this.queue; - int b = batch; - int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1; - if (!exhausted && - ((p = current) != null || (p = q.first()) != null) && - p.next != null) { - Object[] a = new Object[n]; - int i = 0; - do { - if ((a[i] = p.item) != null) - ++i; - if (p == (p = p.next)) - p = q.first(); - } while (p != null && i < n); - if ((current = p) == null) - exhausted = true; - if (i > 0) { - batch = i; - return Spliterators.spliterator - (a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL | - Spliterator.CONCURRENT); - } - } - return null; - } + @Override + public boolean hasNext() { + return nextNode != null; + } - public void forEachRemaining(Consumer action) { - Node p; - if (action == null) throw new NullPointerException(); - final SomeQueue q = this.queue; - if (!exhausted && - ((p = current) != null || (p = q.first()) != null)) { - exhausted = true; - do { - E e = p.item; - if (p == (p = p.next)) - p = q.first(); - if (e != null) - action.accept(e); - } while (p != null); - } - } + @Override + public E next() { + if (nextNode == null) throw new NoSuchElementException(); + return advance(); + } - public boolean tryAdvance(Consumer action) { - Node p; - if (action == null) throw new NullPointerException(); - final SomeQueue q = this.queue; - if (!exhausted && - ((p = current) != null || (p = q.first()) != null)) { - E e; - do { - e = p.item; - if (p == (p = p.next)) - p = q.first(); - } while (e == null && p != null); - if ((current = p) == null) - exhausted = true; - if (e != null) { - action.accept(e); - return true; - } - } - return false; - } + @Override + public void remove() { + Node l = lastRet; + if (l == null) throw new IllegalStateException(); + // rely on a future traversal to relink. + l.item = null; + lastRet = null; + } + } + + /** + * Saves this queue to a stream (that is, serializes it). + * + * @param s the stream + * @throws java.io.IOException if an I/O error occurs + * @serialData All of the elements (each an {@code E}) in the proper order, followed by a null + */ + private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { + + // Write out any hidden stuff + s.defaultWriteObject(); + + // Write out all elements in the proper order. + for (Node p = first(); p != null; p = succ(p)) { + Object item = p.item; + if (item != null) s.writeObject(item); + } - public long estimateSize() { return Long.MAX_VALUE; } + // Use trailing null as sentinel + s.writeObject(null); + } + + /** + * Reconstitutes this queue from a stream (that is, deserializes it). + * + * @param s the stream + * @throws ClassNotFoundException if the class of a serialized object could not be found + * @throws java.io.IOException if an I/O error occurs + */ + private void readObject(java.io.ObjectInputStream s) + throws java.io.IOException, ClassNotFoundException { + s.defaultReadObject(); + + // Read in elements until trailing null sentinel found + Node h = null, t = null; + Object item; + while ((item = s.readObject()) != null) { + @SuppressWarnings("unchecked") + Node newNode = new Node((E) item); + if (h == null) h = t = newNode; + else { + t.lazySetNext(newNode); + t = newNode; + } + } + if (h == null) h = t = new Node(null); + head = h; + tail = t; + } + + /** A customized variant of Spliterators.IteratorSpliterator */ + static final class CLQSpliterator implements Spliterator { + static final int MAX_BATCH = 1 << 25; // max batch array size; + final SomeQueue queue; + Node current; // current node; null until initialized + int batch; // batch size for splits + boolean exhausted; // true when no more nodes + + CLQSpliterator(SomeQueue queue) { + this.queue = queue; + } - public int characteristics() { - return Spliterator.ORDERED | Spliterator.NONNULL | - Spliterator.CONCURRENT; + public Spliterator trySplit() { + Node p; + final SomeQueue q = this.queue; + int b = batch; + int n = (b <= 0) ? 1 : (b >= MAX_BATCH) ? MAX_BATCH : b + 1; + if (!exhausted && ((p = current) != null || (p = q.first()) != null) && p.next != null) { + Object[] a = new Object[n]; + int i = 0; + do { + if ((a[i] = p.item) != null) ++i; + if (p == (p = p.next)) p = q.first(); + } while (p != null && i < n); + if ((current = p) == null) exhausted = true; + if (i > 0) { + batch = i; + return Spliterators.spliterator( + a, 0, i, Spliterator.ORDERED | Spliterator.NONNULL | Spliterator.CONCURRENT); } + } + return null; } - /** - * Returns a {@link Spliterator} over the elements in this queue. - * - *

The returned spliterator is - * weakly consistent. - * - *

The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, - * {@link Spliterator#ORDERED}, and {@link Spliterator#NONNULL}. - * - * @implNote - * The {@code Spliterator} implements {@code trySplit} to permit limited - * parallelism. - * - * @return a {@code Spliterator} over the elements in this queue - * @since 1.8 - */ - @Override - public Spliterator spliterator() { - return new CLQSpliterator(this); + public void forEachRemaining(Consumer action) { + Node p; + if (action == null) throw new NullPointerException(); + final SomeQueue q = this.queue; + if (!exhausted && ((p = current) != null || (p = q.first()) != null)) { + exhausted = true; + do { + E e = p.item; + if (p == (p = p.next)) p = q.first(); + if (e != null) action.accept(e); + } while (p != null); + } } - /** - * Throws NullPointerException if argument is null. - * - * @param v the element - */ - private static void checkNotNull(Object v) { - if (v == null) - throw new NullPointerException(); + public boolean tryAdvance(Consumer action) { + Node p; + if (action == null) throw new NullPointerException(); + final SomeQueue q = this.queue; + if (!exhausted && ((p = current) != null || (p = q.first()) != null)) { + E e; + do { + e = p.item; + if (p == (p = p.next)) p = q.first(); + } while (e == null && p != null); + if ((current = p) == null) exhausted = true; + if (e != null) { + action.accept(e); + return true; + } + } + return false; } - private boolean casTail(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + public long estimateSize() { + return Long.MAX_VALUE; } - private boolean casHead(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + public int characteristics() { + return Spliterator.ORDERED | Spliterator.NONNULL | Spliterator.CONCURRENT; } - - // Unsafe mechanics - - private static final sun.misc.Unsafe UNSAFE; - private static final long headOffset; - private static final long tailOffset; - static { - try { - final Field field = Unsafe.class.getDeclaredField("theUnsafe"); - field.setAccessible(true); - UNSAFE = (Unsafe) field.get(null); - Class k = SomeQueue.class; - headOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("head")); - tailOffset = UNSAFE.objectFieldOffset - (k.getDeclaredField("tail")); - } catch (Exception e) { - throw new Error(e); - } + } + + /** + * Returns a {@link Spliterator} over the elements in this queue. + * + *

The returned spliterator is weakly + * consistent. + * + *

The {@code Spliterator} reports {@link Spliterator#CONCURRENT}, {@link Spliterator#ORDERED}, + * and {@link Spliterator#NONNULL}. + * + * @implNote The {@code Spliterator} implements {@code trySplit} to permit limited parallelism. + * @return a {@code Spliterator} over the elements in this queue + * @since 1.8 + */ + @Override + public Spliterator spliterator() { + return new CLQSpliterator(this); + } + + /** + * Throws NullPointerException if argument is null. + * + * @param v the element + */ + private static void checkNotNull(Object v) { + if (v == null) throw new NullPointerException(); + } + + private boolean casTail(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); + } + + private boolean casHead(Node cmp, Node val) { + return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); + } + + // Unsafe mechanics + + private static final sun.misc.Unsafe UNSAFE; + private static final long headOffset; + private static final long tailOffset; + + static { + try { + final Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + UNSAFE = (Unsafe) field.get(null); + Class k = SomeQueue.class; + headOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("head")); + tailOffset = UNSAFE.objectFieldOffset(k.getDeclaredField("tail")); + } catch (Exception e) { + throw new Error(e); } + } } diff --git a/src/main/java/urlconnection/Test.java b/src/main/java/urlconnection/Test.java index 8ac838d..0cae302 100644 --- a/src/main/java/urlconnection/Test.java +++ b/src/main/java/urlconnection/Test.java @@ -15,53 +15,51 @@ */ public class Test { - public static void main(String[] args) { - System.out.println(sendGet("http://www.baidu.com", "")); - } + public static void main(String[] args) { + System.out.println(sendGet("http://www.baidu.com", "")); + } - public static String sendGet(String url, String param) { - String result = ""; - BufferedReader in = null; - try { - String urlName = url + "?" + param; - URL realUrl = new URL(urlName); - // 打开和URL之间的连接 - URLConnection conn = realUrl.openConnection(); - // 设置通用的请求属性 - conn.setRequestProperty("accept", "*/*"); - conn.setRequestProperty("connection", "Keep-Alive"); - conn.setRequestProperty("user-agent", - "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); - // 建立实际的连接 - conn.connect(); - // 获取所有响应头字段 - Map> map = conn.getHeaderFields(); - // 遍历所有的响应头字段 - for (String key : map.keySet()) { - System.out.println(key + "--->" + map.get(key)); - } - // 定义BufferedReader输入流来读取URL的响应 - in = new BufferedReader( - new InputStreamReader(conn.getInputStream())); - String line; - while ((line = in.readLine()) != null) { - result += "\n" + line; - } - } catch (Exception e) { - System.out.println("发送GET请求出现异常!" + e); - e.printStackTrace(); - } - // 使用finally块来关闭输入流 - finally { - try { - if (in != null) { - in.close(); - } - } catch (IOException ex) { - ex.printStackTrace(); - } + public static String sendGet(String url, String param) { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try { + String urlName = url + "?" + param; + URL realUrl = new URL(urlName); + // 打开和URL之间的连接 + URLConnection conn = realUrl.openConnection(); + // 设置通用的请求属性 + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty( + "user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)"); + // 建立实际的连接 + conn.connect(); + // 获取所有响应头字段 + Map> map = conn.getHeaderFields(); + // 遍历所有的响应头字段 + for (String key : map.keySet()) { + System.out.println(key + "--->" + map.get(key)); + } + // 定义BufferedReader输入流来读取URL的响应 + in = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = in.readLine()) != null) { + result.append("\n" + line); + } + } catch (Exception e) { + System.out.println("发送GET请求出现异常!" + e); + e.printStackTrace(); + } + // 使用finally块来关闭输入流 + finally { + try { + if (in != null) { + in.close(); } - return result; + } catch (IOException ex) { + ex.printStackTrace(); + } } - + return result.toString(); + } } diff --git a/target/classes/META-INF/MANIFEST.MF b/target/classes/META-INF/MANIFEST.MF new file mode 100644 index 0000000..5e1658a --- /dev/null +++ b/target/classes/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: nio.Client +