本文共總結了5種方式,經過代碼舉例的方式進行展現。5種方式爲:synchronized加wait/notify方式、ReentrantLock加Condition方式、閉鎖的方式、柵欄的方式、信號量的方式。java
最後還介紹了join和yield的使用。ide
一、synchronized加wait/notify方式ui
/** * wait和notify的使用 * wait和notify必須應用在synchronized塊或方法內 * 下面的代碼向跳交誼舞同樣互相控制着對方的輸出 */ public class MutiThread_WaitNotify { public static void main(String[] args) { final Object lock = new Object(); Thread a = new Thread(new Runnable(){ @Override public void run(){ synchronized (lock){ try{ lock.wait(); System.out.println("A-1"); lock.notify(); lock.wait(); System.out.println("A-2"); lock.notify(); lock.wait(); System.out.println("A-3"); lock.notify(); }catch(InterruptedException e){ e.printStackTrace(); } } } }); Thread b = new Thread(new Runnable(){ @Override public void run(){ synchronized (lock){ try{ System.out.println("B-1"); lock.notify(); lock.wait(); System.out.println("B-2"); lock.notify(); lock.wait(); System.out.println("B-3"); lock.notify(); lock.wait(); System.out.println("B-4"); }catch(InterruptedException e){ e.printStackTrace();; } } } }); a.start(); b.start(); } }
二、ReentrantLock加Condition方式線程
/** * ReentrantLock和Condition的使用 * 在使用Conditioin的await和signal時,必須將這兩個方法寫在ReentrantLock的lock方法以後 */ public class MutiThread_ReentrantLock_Condition { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); int i=1; for(; i<=6; i++){ final int k = i; Thread t1 = new Thread(new Runnable() { @Override public void run() { try{ lock.lock(); System.out.println("ThreadNo:A" + k + " is locked"); // 經過condition.await將線程阻塞 condition.await(); }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock(); System.out.println("ThreadNo:A"+k + " is unlocked"); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { if(k == 6){ try{ lock.lock(); System.out.println("All Threads is signaled"); // 經過condition.signalAll喚醒全部線程 condition.signalAll(); }catch(Exception e){ e.printStackTrace(); }finally{ lock.unlock(); } }else{ System.out.println("threads can't signaled, wait a moment."); } } }); t1.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } } }
三、閉鎖方式事件
import java.util.concurrent.CountDownLatch; /** * 閉鎖的使用 * 閉鎖用於等待事件,當閉鎖到達結束狀態(本例中是閉鎖的計數器值減爲0)以前,全部線程都等待,當閉鎖到達結束狀態時,全部線程都經過 * 閉鎖是一次性的,當閉鎖到達結束狀態後,將不會被重置,這個鎖會永遠打開並容許全部線程經過。 * 能夠將代碼中的NUM變量值變爲2和4,分別試試什麼效果 */ public class MutiThread_CountDownLatch { public static void main(String[] args) { // 定義閉鎖,並設置閉鎖的計數器值爲3 CountDownLatch lock = new CountDownLatch(3); // 循環定義3個線程 int NUM = 3; for(int i=1; i<=NUM; i++){ final int k = i; Thread a = new Thread(new Runnable(){ @Override public void run(){ try{ Thread.sleep(k * 1000); System.out.println("ThreadNo:A"+k); // 每一個線程在休眠指定時間後將閉鎖的計數器值減1,當閉鎖的計數器值減到0時,閉所將被打開,從而使第二個循環中的全部線程才能經過 lock.countDown(); // 打印閉鎖計數器的值 System.out.println("ThreadNo:A"+k+"; getCount:"+lock.getCount()); }catch(InterruptedException e){ e.printStackTrace(); } } }); a.start(); } // 循環定義2個線程 for(int i=1; i<=2; i++){ final int k = i; Thread b = new Thread(new Runnable(){ @Override public void run(){ try{ System.out.println("ThreadNo:B"+k+" is waiting..."); // 當閉鎖的計數器值不爲0時,線程將在此處被中斷 lock.await(); // 當閉鎖的計數器值等於0時,閉鎖將被打開,全部等待的線程都將被喚醒 System.out.println("ThreadNo:B"+k+" is notify"); }catch(InterruptedException e){ e.printStackTrace(); } } }); b.start(); } } }
四、柵欄的方式資源
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * 柵欄的使用 * 柵欄用於等待線程,全部線程必須同時到達柵欄,才能繼續執行 * 柵欄不是一次性的,能夠被重置。 * 能夠將代碼中的NUM變量值變爲5和7,分別試試什麼效果 */ public class MutiThread_CyclicBarrier { public static void main(String[] args) { // 定義柵欄,並設置柵欄須要等待的線程數爲6 CyclicBarrier barrier = new CyclicBarrier(6); int NUM = 100; for(int i=1; i<=NUM; i++){ final int k = i; Thread t = new Thread(new Runnable() { @Override public void run() { try{ Thread.sleep(k * 1000); System.out.println("ThreadNo:"+k+" is waiting, getNumberWaiting:" + barrier.getNumberWaiting()); // 柵欄設置的等待線程數爲6,當線程數不夠6個時,全部線程將在此等待 barrier.await(); // 當線程數達到6個時,柵欄將被打開,全部線程都將被喚醒 System.out.println("ThreadNo:"+k+" is notify"); // 柵欄被重置,以便下次繼續使用 barrier.reset(); }catch(InterruptedException e){ e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }); t.start(); } } }
五、信號量的方式get
import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; /** * 信號量的使用 * 信號量用於控制同時訪問某個資源的線程數量,信號量還能夠用於實現某個資源池。 * 信號量管理者一組虛擬的許可,線程在執行操做時首先要得到許可,若是信號量的許可數量爲0,那麼accquire將阻塞直到有許可爲止 * 信號量不是一次性的,當信號鏈的許可用完以後,能夠經過release釋放許可 */ public class MutiThread_Semaphore { public static void main(String[] args) { // 定義信號量,並設置信號量的容許發放的最大許可數量爲6 final Semaphore semaphore = new Semaphore(6); // 定義集合,當信號量未發放的許可數量大於0則容許線程向集合內添加元素 final List<String> set = new ArrayList<>(); int i = 1; while(true){ final int k = i++; Thread t = new Thread(new Runnable() { @Override public void run() { boolean res = false; try{ System.out.println("ThreadNo:A"+k+", availablePermits:"+semaphore.availablePermits()); // 當信號量容許發放的許可數量大於0,則會向集合內添加元素,不然將被中斷於此 semaphore.acquire(); res = set.add("1"); System.out.println("ThreadNo:A"+k+" add item success"); }catch(InterruptedException e){ e.printStackTrace(); }finally{ if(!res){ semaphore.release(); } } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { if(semaphore.availablePermits() == 0){ // 若是信號量容許發放的許可數量等於0,則釋放制定數量的許可 semaphore.release(3); //釋放3個許可 System.out.println("ThreadNo:B"+k+" releasePermitNum:"+semaphore.availablePermits()); } } }); t.start(); t2.start(); System.out.println("the num of set:"+set.size()); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
A、join的使用it
/** * join的使用 * 實現當調用join的線程執行完畢後,其餘線程才能執行 */ public class MutiThread_Join { public static void main(String[] args) { Thread a = new Thread(new Runnable(){ @Override public void run(){ printNumber("A"); } }); Thread b = new Thread(new Runnable(){ @Override public void run(){ printNumber("B"); } }); try{ a.start(); // a線程執行完畢後,b線程才能執行 a.join(); b.start(); }catch(InterruptedException e){ e.printStackTrace();; } } public static void printNumber(String s){ System.out.println(s+" print:"+s); } }
B、yield的使用io
/** * yield,當一個線程中調用了這個方法後,這個線程就會把本身的CPU執行時間讓給本身或其它線程, * 注意是讓給本身或其它線程,並非單純讓給其餘線程。yield執行後,能讓當前線程由運行狀態 * 進入到就緒狀態,將本身的CPU時間片讓出來,讓出來以後有多是其它線程執行,也有多是該線程 * 繼續執行。優先級高的線程並不必定是首先執行,而是首先執行的機率會高一些。優先級在大量線程 * 執行的時候才能體現的出來。 */ public class MutiThread_yield { public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ System.out.println("ThreadNo:A"+i); Thread.yield(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { for(int i=0;i<10;i++){ System.out.println("ThreadNo:B"+i); Thread.yield(); } } }); t1.setPriority(Thread.MIN_PRIORITY); t2.setPriority(Thread.MAX_PRIORITY); t1.start(); t2.start(); } }