實戰體會多線程,實戰多線程
線程
首先瞭解線程的五大狀態:建立,就緒,運行,阻塞,終結。
1、建立狀態(New):新建立了一個線程對象。
2、就緒狀態(Runnable):線程對象建立後,其他線程調用了該對象的start()方法。該狀態的線程位於“可運行線程池”中,變得可運行,只等待擷取CPU的使用權。即在就緒狀態的進程除CPU之外,其它的運行所需資源都已全部獲得。
3、運行狀態(Running):就緒狀態的線程擷取了CPU,執行程式代碼。
4、阻塞狀態(Blocked):阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。
阻塞的情況分三種:
(1)、等待阻塞:啟動並執行線程執行wait()方法,該線程會釋放佔用的所有資源,JVM會把該線程放入“等待池”中。進入這個狀態後,是不能自動喚醒的,必須依靠其他線程調用notify()或notifyAll()方法才能被喚醒,
(2)、同步阻塞:啟動並執行線程在擷取對象的同步鎖時,若該同步鎖被別的線程佔用,則JVM會把該線程放入“鎖池”中。
(3)、其他阻塞:啟動並執行線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態逾時、join()等待線程終止或者逾時、或者I/O處理完畢時,線程重新轉入就緒狀態。
5、死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命週期。
線程變化的狀態轉換圖如下:
線程的實現方法
實現有兩種方式,一是繼承Thread類,二是實現Runnable介面,但不管怎樣, 當我們new了這個對象後,線程就進入了初始狀態; 當該對象調用了start()方法,就進入就緒狀態!
在工作中,因為需要擷取傳回值的線程,接觸到了callable介面
Callable需要實現的是call()方法,而不是run()方法,傳回值的類型有Callable的型別參數指定, Callable只能由ExecutorService.submit() 執行,正常結束後將返回一個future對象,執行個體如下:
ExecutorService pool = Executors.newFixedThreadPool(10); Callable<String> c1 = new YkuploadRunable(");Future<String> f1 = pool.submit(c1); System.out.println(f1.get()); pool.shutdown();
線程的經典問題:生產者和消費者問題
生產者可以將產品放入倉庫,消費者則可以從倉庫中取走產品,要有保護倉庫為空白或溢出
解決生產者/消費者問題的方法可分為兩類:
1.採用某種機制保護生產者和消費者之間的同步;
2.在生產者和消費者之間建立一個管道。
實現方法有
1.object.wait(),object.notify();
2.condition.await(),condition.signal();
3.LinkedBlockingQueue<Object> list; put(),take()會自動阻塞
方法一的具體代碼
倉庫.java
public class Storage{ // 倉庫最大儲存量 private final int MAX_SIZE = 100; // 倉庫儲存的載體 private LinkedList<Object> list = new LinkedList<Object>(); // 生產產品 public void produce(String producer) { synchronized (list) { // 如果倉庫已滿 while (list.size() == MAX_SIZE) { System.out.println("倉庫已滿,【"+producer+"】: 暫時不能執行生產任務!"); try { // 由於條件不滿足,生產阻塞 list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 生產產品 list.add(new Object()); System.out.println("【"+producer+"】:生產了一個產品\t【現倉儲量為】:" + list.size()); list.notifyAll(); } } // 消費產品 public void consume(String consumer) { synchronized (list) { //如果倉庫儲存量不足 while (list.size()==0) { System.out.println("倉庫已空,【"+consumer+"】: 暫時不能執行消費任務!"); try { // 由於條件不滿足,消費阻塞 list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } list.remove(); System.out.println("【"+consumer+"】:消費了一個產品\t【現倉儲量為】:" + list.size()); list.notifyAll(); } } public LinkedList<Object> getList() { return list; } public void setList(LinkedList<Object> list) { this.list = list; } public int getMAX_SIZE() { return MAX_SIZE; }}
生產者.java
public class Producer extends Thread{ private String producer; private Storage storage; public Producer(Storage storage) { this.storage = storage; } @Override public void run() { produce(producer); } public void produce(String producer) { storage.produce(producer); } public String getProducer() { return producer; } public void setProducer(String producer) { this.producer = producer; } public Storage getStorage() { return storage; } public void setStorage(Storage storage) { this.storage = storage; }}
消費者.java
public class Consumer extends Thread{ private String consumer; private Storage storage; public Consumer(Storage storage) { this.storage = storage; } @Override public void run() { consume(consumer); } public void consume(String consumer) { storage.consume(consumer); } public Storage getStorage() { return storage; } public void setStorage(Storage storage) { this.storage = storage; } public String getConsumer() { return consumer; } public void setConsumer(String consumer) { this.consumer = consumer; }}
方法二的實現如下:注意lock的lock(),unlock()
public class Storage { // 倉庫最大儲存量 private final int MAX_SIZE = 100; // 倉庫儲存的載體 private LinkedList<Object> list = new LinkedList<Object>(); // 鎖 private final Lock lock = new ReentrantLock(); // 倉庫滿的條件變數 private final Condition full = lock.newCondition(); // 倉庫空的條件變數 private final Condition empty = lock.newCondition(); // 生產產品 public void produce(String producer) { lock.lock(); // 如果倉庫已滿 while (list.size() == MAX_SIZE) { System.out.println("倉庫已滿,【" + producer + "】: 暫時不能執行生產任務!"); try { // 由於條件不滿足,生產阻塞 full.await(); } catch (InterruptedException e) { e.printStackTrace(); } } // 生產產品 list.add(new Object()); System.out.println("【" + producer + "】:生產了一個產品\t【現倉儲量為】:" + list.size()); empty.signalAll(); // 釋放鎖 lock.unlock(); } // 消費產品 public void consume(String consumer) { // 獲得鎖 lock.lock(); // 如果倉庫儲存量不足 while (list.size() == 0) { System.out.println("倉庫已空,【" + consumer + "】: 暫時不能執行消費任務!"); try { // 由於條件不滿足,消費阻塞 empty.await(); } catch (InterruptedException e) { e.printStackTrace(); } } list.remove(); System.out.println("【" + consumer + "】:消費了一個產品\t【現倉儲量為】:" + list.size()); full.signalAll(); // 釋放鎖 lock.unlock(); } public LinkedList<Object> getList() { return list; } public void setList(LinkedList<Object> list) { this.list = list; } public int getMAX_SIZE() { return MAX_SIZE; }}
方法三的實現如下:
import java.util.concurrent.LinkedBlockingQueue;public class Storage { // 倉庫最大儲存量 private final int MAX_SIZE = 100; // 倉庫儲存的載體 private LinkedBlockingQueue<Object> list = new LinkedBlockingQueue<Object>(100); // 生產產品 public void produce(String producer) { // 如果倉庫已滿 if (list.size() == MAX_SIZE) { System.out.println("倉庫已滿,【" + producer + "】: 暫時不能執行生產任務!"); } // 生產產品 try { list.put(new Object()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("【" + producer + "】:生產了一個產品\t【現倉儲量為】:" + list.size()); } // 消費產品 public void consume(String consumer) { // 如果倉庫儲存量不足 if (list.size() == 0) { System.out.println("倉庫已空,【" + consumer + "】: 暫時不能執行消費任務!"); } try { list.take(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("【" + consumer + "】:消費了一個產品\t【現倉儲量為】:" + list.size()); } public LinkedBlockingQueue<Object> getList() { return list; } public void setList(LinkedBlockingQueue<Object> list) { this.list = list; } public int getMAX_SIZE() { return MAX_SIZE; }}
線程同步
同步鎖synchronized
上面的wait(),await()
還有一種join方法,使線程順序進行。
線程實戰
因為需要在controller中同時上傳多個視頻並儲存返回的視頻id進資料庫,所以在controller中寫了一個內部類,具體代碼如下:
class Ykupload implements Runnable {@Overridepublic void run() {System.out.println(tpVideoLibrary.toString());YkuploadRunable yp = new YkuploadRunable();//擷取yp的資料並寫進資料庫}}
後記:貌似不能同時上傳同一個檔案,多個視頻上傳速度還是取決於你的頻寬!