常見的操做系統教科書中,會使用互斥鎖來實現讀者線程和寫者線程的同步問題,可是在JDK5推出線程安全隊列以後,將該問題變得異常簡單。 java
java.util.concurrent.ConcurrentLinkedQueue 是線程安全的非阻塞隊列,其實很容易想到,非阻塞隊列當線程須要等待的時候,則不會阻塞等待,而是直接根據狀況返回。 安全
java.util.concurrent.LinkedBlockingQueue 是線程安全的阻塞隊列,該隊列可以在不少狀況下對線程進行阻塞,好比隊列爲空時調用take() 方法,改方法是從隊首取得對象,若是對象不存在,則會阻塞等待,直到有可取的對象以後則喚醒該線程並取走隊首對象。一樣,這種阻塞隊列還有put() 這樣的阻塞方法:在放入對象到隊尾的時候若是隊列不可用,則阻塞等待,知道隊列可用。而非阻塞隊列則沒有這樣的阻塞方法。 ide
下面這個例子,是使用非阻塞隊列實現讀者寫者問題的簡單例子: this
package nonblockingqueueconpro; import java.util.concurrent.ConcurrentLinkedQueue; public class MainThread { public static void main(String args[]) { ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<String>(); for (int i = 0; i < 2; i++) { Productor task = new Productor(queue); Thread t = new Thread(task, "生產者線程" + i); t.start(); } for (int i = 0; i < 2; i++) { Consumer task = new Consumer(queue); Thread t = new Thread(task, "消費者線程" + i); t.start(); } } } class Consumer implements Runnable { private ConcurrentLinkedQueue<String> queue; public Consumer(ConcurrentLinkedQueue<String> queue) { this.queue = queue; } @Override public void run() { while (true) { try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("消費者--> " + queue.poll()); } } } class Productor implements Runnable { private ConcurrentLinkedQueue<String> queue; public Productor(ConcurrentLinkedQueue<String> queue) { this.queue = queue; } @Override public void run() { while (true) { try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String content = String.valueOf(System.currentTimeMillis()); System.out.println("生產者==> " + content); queue.add(content); } } }
消費者--> null 消費者--> null 生產者==> 1439301233285 生產者==> 1439301233284 消費者--> 1439301233284 生產者==> 1439301234289 生產者==> 1439301234289 ......
而使用阻塞隊列設計的例子,消費者不會出現返回null的狀況,若是隊列爲空時,消費者線程會進行等待,代碼以下: 操作系統
package blockingqueueconpro; import java.util.concurrent.LinkedBlockingQueue; public class MainThread { public static void main(String args[]) { LinkedBlockingQueue<String> queue = new LinkedBlockingQueue<String>(); for (int i = 0; i < 2; i++) { Productor task = new Productor(queue); Thread t = new Thread(task, "生產者線程" + i); t.start(); } for (int i = 0; i < 2; i++) { Consumer task = new Consumer(queue); Thread t = new Thread(task, "消費者線程" + i); t.start(); } } } class Productor implements Runnable { private LinkedBlockingQueue<String> queue; public Productor(LinkedBlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { while (true) { try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } String content = String.valueOf(System.currentTimeMillis()); System.out.println("生產者==> " + content); queue.add(content); } } } class Consumer implements Runnable { private LinkedBlockingQueue<String> queue; public Consumer(LinkedBlockingQueue<String> queue) { this.queue = queue; } @Override public void run() { while (true) { try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } try { System.out.println("消費者--> " + queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
生產者==> 1439301385710 生產者==> 1439301385710 消費者--> 1439301385710 消費者--> 1439301385710 生產者==> 1439301386716 生產者==> 1439301386716 ......
總結:在平常開發中,須要線程安全的狀況下,java.util.concurrent 包下面的不少類可以解決問題,這比起本身實現對象鎖和同步來講,代碼更簡單,也更安全。 線程