Java基礎--併發程式設計
阿新 • • 發佈:2018-11-21
JAVA併發程式設計第一步:瞭解Java虛擬機器提供的API操作,以及執行緒基本概念的操作:
1:定義執行緒 -- 繼承Thread類和實現Runnable方法
1 /** 2 * 定義執行緒 1:繼承Thread類,方法、形式如下 3 */ 4 public static class T1 extends Thread{ 5 @Override 6 public void run() { 7 System.out.println("Threads.T1.run()"); 8 }9 10 public static void main(String[] args) { 11 /** 12 * 啟動執行緒:只需要使用new關鍵字建立一個執行緒物件,並且將它 start()起來即可。 13 */ 14 T1 t1 = new T1(); 15 Thread thread = new Thread(t1); 16 thread.start(); 17 T2 t2 = new T2(); 18Thread thread2 = new Thread(t2); 19 thread2.start(); 20 } 21 } 22 /** 23 *定義執行緒 實現runnable介面,方法、形式如下 24 */ 25 public static class T2 implements Runnable{ 26 27 public void run() { 28 System.out.println("Threads.t2.run()"); 29 }30 31 }
2:執行緒終止-- 不要用 API 提供的 stop() , stop太暴力,應自己寫邏輯實現
public static class StopThread implements Runnable{ volatile boolean stopme = false; public void stopeMe(){ stopme = true; } public void run() { while (true) { if (stopme) { System.out.println("exit by stop me"); break; } } } }
3:執行緒中斷
/** * 執行緒中斷 * public void Thread.interrupt() //中斷執行緒 * Public void boolean Thread.isInterrupted() //判斷執行緒是否被中斷 * Public static boolean Thread.interrupted() //判斷是否被中斷,並請除當前中斷狀態 */ public static class InterrupThread implements Runnable{ public void run() { while(true){ try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Thread running"); } } public static void main(String[] args) { /** * 啟動執行緒:只需要使用new關鍵字建立一個執行緒物件,並且將它start()起來即可。 */ InterrupThread interrupThread = new InterrupThread(); Thread thread = new Thread(interrupThread); thread.start(); // Thread.sleep(1000); /** * thread 會 產生中斷,但是並沒有處理中斷,遇到 sleep 丟擲異常 * 當程式出現 wait 和sleep 的時候,中斷會被忽略掉 * 如果執行緒執行到了sleep()程式碼段,主程式中斷執行緒,執行緒這這時候丟擲異常, * 進入catch的異常處理。在catch程式碼段中,由於捕獲到了中斷,我們可以立即退出執行緒。 * 在這裡我們並沒有這麼做,因為也許在這段程式碼中,我們還必須進行後續處理,保證資料的一致性和完整性, * 因此,執行了Thread.interrupt()方法在次中斷自己,設定中斷標誌位。 */ thread.interrupt(); System.out.println(thread.isInterrupted()); } }
4:等待和通知
public class WaitAndNotify { /** * JDK提供了兩個非常重要的介面,執行緒等待wait()方法和執行緒通知方法notify()。這兩個方法不是在Thread類中的 * object.notifyAll()方法,他會喚醒等待佇列中的所有執行緒。 */ final static Object object = new Object(); public static class MyThread_1 extends Thread{ @Override public void run(){ synchronized (object) { System.out.println(System.currentTimeMillis()+"T1 start"); try { System.out.println(System.currentTimeMillis()+"T1 wait"); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(System.currentTimeMillis()+"T1 end"); } } } public static class MyThread_2 extends Thread { @Override public void run() { synchronized (object) { System.out.println(System.currentTimeMillis() + "T2 start and notify"); object.notify(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Thread t1 = new MyThread_1(); Thread t2 = new MyThread_2(); t1.start(); t2.start(); } }
5:volatile關鍵字解析
public class VolatileApp { /** * 這裡的 ready 和 number 相當於 被 volatile 修飾的變數 * volatile來宣告一個變數時,就等於告訴了虛擬機器,這個變數極有可能會被某些程式或者是執行緒修改。 * 為了確保這個變數被修改後,應用程式範圍內所有執行緒都能看到這個改動,虛擬機器就必須採取一些特殊的手段,保證這個變數的可見性等特點。 */ private static boolean ready; private static int number; public static void main(String[] args) throws InterruptedException { MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start(); thread.sleep(1000); ready = false; number = 100; thread.sleep(2000); } public static class MyThread implements Runnable{ public void run() { while(!ready) System.out.println(number); } } }
6:執行緒組概念
public class ThreadGroupApp { /** * 構造方法: * * ThreadGroup(String name):以指定執行緒組名字來建立新執行緒組 * * ThreadGroup(ThreadGroup parent,String name):以指定的名字、指定的父執行緒組來建立一個新執行緒組。 * * 常用操作方法: * * · int activeCount():獲取執行緒組中活動執行緒的數量 * * · interrupt():中斷執行緒組中所有執行緒 * * · isDaemon():是否為後臺執行緒組 * * · setDaemon(boolean daemon):設定為後臺執行緒組 * * · setMaxPriority(int pri):設定執行緒組的最高優先順序 * * 執行緒都是在創造的同時加入執行緒組中,然後才start。上述程式碼展示了執行緒組的兩個重要功能, * * activeCount()可以獲得活動執行緒的總數,但由於執行緒是動態的,所以這個值是一個預估值, * * list()可以列印這個執行緒組中所有的執行緒資訊,對除錯有一定幫助。 */ public static void main(String[] args) { ThreadGroup threadGroup = new ThreadGroup("ThreadGroupApp"); MyThread myThread_1 = new MyThread(threadGroup,"myThread_1"); MyThread myThread_2 = new MyThread(threadGroup,"myThread_2"); myThread_1.setDaemon(true);//設定守護執行緒 ,必須在start 之前執行 myThread_1.setPriority(1);//設定執行緒優先順序 1-10 myThread_1.start(); myThread_2.start(); System.out.println(threadGroup.activeCount()); threadGroup.list(); } public static class MyThread extends Thread{ public MyThread(String name){ super(name); } public MyThread(ThreadGroup group,String name){ super(group, name); } @Override public void run() { super.run(); } } }
7:synchronized 執行緒安全概念
/** * 執行緒安全 synchronized * 除了執行緒同步,確保執行緒安全, synchronized 確保執行緒之間可見、有序 * 從可見性的角度上講,synchronized完全可以代替volatile的功能,只是使用上沒有那麼方便。就有序性而言, * 由於synchronized每一次只有一個執行緒可以訪問同步塊,因此,無論同步塊內的程式碼如何被亂序執行,只要保證序列語義一致,那麼執行的結果總是一樣的。 * 換而言之,被synchronized限制的多個執行緒是序列執行的。 */ public class ThreadSafeApp { public static int number; public static void main(String[] args) throws InterruptedException { AddThread addThread_1 = new AddThread(); AddThread addThread_2 = new AddThread(); Thread thread_1 = new Thread(addThread_1); Thread thread_2 = new Thread(addThread_2); thread_1.start(); thread_2.start(); thread_1.join(); thread_2.join(); // 等待執行緒結束 System.out.println(number); } public static class AddThread implements Runnable{ /** * 牽扯到記憶體模型,每一個類 new 的時候會生成方法區,在堆中獨立的空間所以宣告synchronized 同步方法只是在這個類中構造獨立,那麼兩個記憶體空間還是相互影響 * 資料不一致 -- 解決辦法為:設定成靜態 */ public static synchronized void add(){ number++; } public void run() { // synchronized (AddThread.class) { for (int i = 0; i < 10000; i++) { // number++; add(); } } // } } }
8:重入鎖
public class LockApp { public static ReentrantLock Lock = new ReentrantLock(); public static int Count = 0; public static void main(String[] args) throws InterruptedException { ThreadLock t1 = new ThreadLock(); ThreadLock t2 = new ThreadLock(); t1.start();t2.start(); t1.join();t2.join(); System.out.println(Count); } public static class ThreadLock extends Thread{ @Override public void run() { for(int i=0;i<10000;i++) { /** * 必須手動指定何時加鎖 ,何時釋放鎖。也正是因為這樣, * 重入鎖邏輯控制遠遠要好於synchronized。 * 但值得注意的是,在退出零界區時,必須記得要釋放鎖,否者永遠沒有機會再訪問零界區了,會造成其執行緒的飢餓甚至是死鎖。 * 針對 lock() 可以多次呼叫! 但是必須釋放 */ Lock.lock(); Count++; Lock.unlock(); } } } }
9:lockInterruptibly 重入鎖的中斷
/** * 重入鎖的中斷 lockInterruptibly * 優先考慮響應中斷,而不是響應鎖的普通獲取或重入獲取。 */ public class ThreadInterruptibilyApp { public static ReentrantLock Lock1 = new ReentrantLock(); public static ReentrantLock Lock2 = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { MyThread t1 = new MyThread(1); MyThread t2 = new MyThread(2); t1.start(); t2.start(); Thread.sleep(3000); t2.interrupt(); /** * 在t1和t2執行緒start後,主執行緒main進入休眠,此時t1和t2執行緒處於死鎖狀態, * 然後主執行緒main中斷t2執行緒, * 故t2會放棄對lock1的請求,同時釋放lock2。這個操作使得t1可以獲得lock2從而繼續執行下去。 * 那麼,完成工作的只有t1 ,t2 是直接放棄推出,釋放資源 */ } public static class MyThread extends Thread{ int flag; MyThread(int flag) { this.flag = flag; } @Override public void run() { try{ if(flag == 1){ try { Lock1.lockInterruptibly(); Thread.sleep(1000); Lock2.lockInterruptibly(); System.out.println(flag+"號執行緒:完成工作"); } catch (InterruptedException e) {} } else if(flag == 2){ try { Lock2.lockInterruptibly(); Thread.sleep(1000); Lock1.lockInterruptibly(); System.out.println(flag+"號執行緒:完成工作"); } catch (InterruptedException e) {} } }finally{ //中斷響應 if(Lock1.isHeldByCurrentThread()){ Lock1.unlock(); System.out.println(flag+":Lock1 interrupted unlock"); } if(Lock2.isHeldByCurrentThread()){ Lock2.unlock(); System.out.println(flag+":Lock2 interrupted unlock"); } System.out.println(flag+"號執行緒退出"); } } } }
10:Map、ArrayList、Vector、ConcurrenHashMap 執行緒安全測試
public class ThreadErrorApp { public static Map<String,String> threadMap = new HashMap<String, String>(); public static ArrayList<Integer> threadArrayList = new ArrayList<Integer>(); public static Vector<Integer> threadVector = new Vector<Integer>(); public static void main(String[] args) throws InterruptedException { // MyThread myThread_1 = new MyThread(); // MyThread myThread_2 = new MyThread(); // Thread thread_1 = new Thread(myThread_1); // Thread thread_2 = new Thread(myThread_2); // thread_1.start(); // thread_2.start(); // thread_1.join(); // thread_2.join(); // System.out.println(threadVector.size()); MyThreadMap myThread_1 = new MyThreadMap(); MyThreadMap myThread_2 = new MyThreadMap(); Thread thread_1 = new Thread(myThread_1); Thread thread_2 = new Thread(myThread_2); thread_1.start(); thread_2.start(); thread_1.join(); thread_2.join(); System.out.println(threadMap.size()); } /** * 1: 報錯java.lang.ArrayIndexOutOfBoundsException 因為記憶體一致性 遭到破壞,產生越界問題 * 2:size 小於 20000 * 解決辦法 使用Vector */ public static class MyThread extends Thread { @Override public void run() { for (int i=0;i<10000;i++){ // threadArrayList.add(i); threadVector.add(i); } } } /** * 程式正常結束,但結果不符合預期,而是一個小於100000的數字。 * 使用 ConcurrenHashMap 代替 */ public static class MyThreadMap extends Thread{ @Override public void run() { for (int i=0;i<10000;i++){ threadMap.put(Integer.toString(i),Integer.toString(i)); } } } }