1 package queue; 2 3 /** 4 * 佇列介面 5 * @author jzj 6 * 7 * @param <E> 8 */ 9 public interface Queue<E> { 10 //入隊 11 public void enqueue(E e); 12 13 //出隊 14 public E dequeue(); 15 16 //取佇列第一個 17 public E front(); 18 19 //佇列是否為空 20 public boolean isEmpty(); 21 22 //佇列大小 23 public int size(); 24 }
1 package queue; 2 3 /** 4 * 迴圈佇列,使用陣列實現 5 * @author jzj 6 */ 7 public class ArrayQueue<E> implements Queue<E> { 8 9 private Object lock = new Object(); 10 11 // 佇列大小 12 private int size = 1000; 13 14 // 佇列 15 private E[] arrStr = (E[]) new Object[size]; 16 17 // 佇列指標 18 private int head = 0; 19 20 // 佇列尾指標 21 private int tail = 0; 22 23 /** 24 * 入隊 25 * @param o 26 */ 27 public void enqueue(E o) { 28 synchronized (lock) { 29 30 // 如果佇列已滿 31 while ((tail + 1) % size == head) { 32 try { 33 System.out.println("佇列已滿," + Thread.currentThread().getName() 34 + " 執行緒阻塞..."); 35 // 佇列滿時執行緒阻塞 36 lock.wait();//注,這裡一定要放在while條件裡,因為獲取鎖後,條件不一定還成立 37 } catch (InterruptedException e) { 38 e.printStackTrace(); 39 } 40 } 41 // 如果佇列未滿 42 arrStr[tail] = o; 43 // 指標下移 44 tail = (tail + 1) % size; 45 // 入隊後通知消費執行緒 46 lock.notifyAll(); 47 } 48 } 49 50 /** 51 * 出隊 52 * @return 53 */ 54 public E dequeue() { 55 synchronized (lock) { 56 // 如果佇列為空 57 while (head == tail) { 58 try { 59 System.out.println("佇列為空," + Thread.currentThread().getName() 60 + " 執行緒阻塞..."); 61 // 佇列空時執行緒阻塞 62 lock.wait();//注,這裡一定要放在while條件裡,因為獲取鎖後,條件不一定還成立 63 } catch (InterruptedException e) { 64 e.printStackTrace(); 65 } 66 } 67 // 佇列非空 68 E tempStr = arrStr[head]; 69 70 arrStr[head] = null;//注,這裡出隊後釋放物件,加快回收,不然大的物件可能造記憶體洩露 71 head = (head + 1) % size; 72 73 // 出隊後通知生產者 74 lock.notifyAll(); 75 return tempStr; 76 77 } 78 } 79 80 //取佇列第一個 81 public E front() { 82 synchronized (lock) { 83 // 如果佇列為空 84 while (head == tail) { 85 try { 86 System.out.println("佇列為空," + Thread.currentThread().getName() 87 + " 執行緒阻塞..."); 88 // 佇列空時執行緒阻塞 89 lock.wait();//注,這裡一定要放在while條件裡,因為獲取鎖後,條件不一定還成立 90 } catch (InterruptedException e) { 91 e.printStackTrace(); 92 } 93 } 94 // 佇列非空 95 return arrStr[head]; 96 } 97 } 98 99 //佇列是否為空 100 public boolean isEmpty() { 101 return head == tail; 102 } 103 104 //佇列大小 105 public int size() { 106 107 return Math.abs(tail - head); 108 } 109 }
Jdk1.5已有java.util.Queue介面
使用1.5中併發新特性實現迴圈佇列:來源於 API java.util.concurrent.locks.Condition
1 //迴圈緩衝佇列,從隊首放,從隊尾出。犧牲陣列的最後一個元素,用來判斷佇列是否滿 2 class BoundedBuffer { 3 final Lock lock = new ReentrantLock();//可重入鎖 4 final Condition notFull = lock.newCondition();//緩衝空條件物件 5 final Condition notEmpty = lock.newCondition();//緩衝非空條件物件 6 7 final Object[] items = new Object[100];//緩衝區 8 int putptr/*隊首*/, takeptr/*隊尾*/, count/*可用個數*/; 9 10 //將元素放入緩衝,供生產執行緒呼叫 11 public void put(Object x) throws InterruptedException { 12 lock.lock();//獲取鎖 13 try { 14 //注,要將await置於迴圈中,這也wait道理一樣 15 while (count == items.length) 16 notFull.await();//緩衝滿時等待,且釋放鎖 17 items[putptr] = x; 18 //如果隊首指標指向陣列最後索引時,重新指向陣列第一個元素位置 19 if (++putptr == items.length) 20 putptr = 0; 21 ++count;//迴圈佇列中存入的元素個數 22 notEmpty.signal();//放入元素後通知其它消費執行緒可以消費了 23 } finally { 24 lock.unlock();//釋放鎖 25 } 26 } 27 28 //從緩衝取出元素,供消費執行緒呼叫 29 public Object take() throws InterruptedException { 30 lock.lock(); 31 try { 32 while (count == 0) 33 notEmpty.await();//如果緩衝空則等待 34 Object x = items[takeptr]; 35 //如果隊尾指標指向陣列最後索引時,重新指向陣列第一個元素位置 36 if (++takeptr == items.length) 37 takeptr = 0; 38 --count; 39 notFull.signal();//取出後會有空間,通知生產執行緒 40 return x; 41 } finally { 42 lock.unlock(); 43 } 44 } 45 }
1 package queue; 2 3 import java.util.LinkedList; 4 5 /** 6 * 使用 LinkedList 實現佇列 7 * @author jzj 8 * 9 * @param <E> 10 */ 11 public class LinkedListQueue<E> implements Queue<E> { 12 private LinkedList<E> linkedList = new LinkedList<E>(); 13 14 //入隊 15 public void enqueue(E e) { 16 linkedList.addLast(e); 17 } 18 19 //出隊 20 public E dequeue() { 21 return linkedList.removeFirst(); 22 } 23 24 //取佇列第一個 25 public E front() { 26 return linkedList.getFirst(); 27 } 28 29 //佇列是否為空 30 public boolean isEmpty() { 31 return linkedList.isEmpty(); 32 } 33 34 //佇列大小 35 public int size() { 36 return linkedList.size(); 37 } 38 }
1 package queue; 2 3 import java.util.NoSuchElementException; 4 5 /** 6 * 使用 迴圈雙向鏈 實現佇列 7 * 8 * @author jzj 9 * 10 * @param <E> 11 */ 12 public class LinkedQueue<E> implements Queue<E> { 13 private class Entry { 14 E element;//資料域 15 Entry next;//後驅節點 16 Entry previous;//前驅節點 17 18 /** 19 * @param element 資料域 20 * @param next 後驅節點 21 * @param previous 前驅節點 22 */ 23 Entry(E element, Entry next, Entry previous) { 24 this.element = element; 25 this.next = next; 26 this.previous = previous; 27 } 28 } 29 30 /* 31 * 頭節點,永遠代表頭。鏈中無結節時節點中的前後驅指標域指向自己,當有元素時 32 * 頭節點的前驅指標域previous指向鏈中最後一個節點,而next指標域指向鏈中的 33 * 第一個節點 34 */ 35 private Entry header = new Entry(null, null, null); 36 37 private int size = 0;//連結串列中節點個數,但不包括頭節點header 38 39 public LinkedQueue() { 40 //初始化時頭的前後驅指標都指向自己 41 header.next = header.previous = header; 42 } 43 44 //入隊,在連結串列中的最後位置加入元素,相當於 LinkedList 的add、addBefore、addLast方法實現 45 public void enqueue(E e) { 46 //在雙向鏈後面加入元素 47 Entry newEntry = new Entry(e, header, header.previous); 48 //讓新元素的前驅節點(新增前鏈的最後點)的前驅指向新加元素 49 newEntry.previous.next = newEntry; 50 //讓新元素的後驅節點(header頭節點)的前驅指向新加元素 51 newEntry.next.previous = newEntry; 52 size++; 53 } 54 55 //出隊,從連結串列中的第一個元素開始出隊,相當於 LinkedList 的removeFirst方法實現 56 public E dequeue() { 57 //要出隊(刪除)的節點的資料域 58 E first = header.next.element; 59 Entry e = header.next; 60 //要刪除的節點為頭節點時不能刪除 61 if (e == header) { 62 throw new NoSuchElementException(); 63 } 64 65 //將刪除節點的前驅節點的next域指標指向刪除節點的後驅節點 66 e.previous.next = e.next; 67 //將刪除節點的後驅節點的previous域指標指向刪除節點的前驅節點 68 e.next.previous = e.previous; 69 size--; 70 return first; 71 } 72 73 //取佇列的第一個元素,相當於 LinkedList 的getFirst方法實現 74 public E front() { 75 if (size == 0) 76 throw new NoSuchElementException(); 77 78 return header.next.element; 79 } 80 81 //佇列是否為空 82 public boolean isEmpty() { 83 return size == 0; 84 } 85 86 //佇列大小 87 public int size() { 88 return size; 89 } 90 }