前言
進程是計算機中程序關於某幾何數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位。是操作系統結構的基礎
線程可以說是輕量級的進程,是程序執行的最小單位,使用多線程而不用多進程去進行併發程序的設計,是因為線程之間的切換與調度的成本遠小於進程。
線程的幾種狀態
<code>New狀態表示剛剛創建的線程,這種線程還沒有開始執行 RUNNABLE:當線程創建好之後,調用線程的start方法就會進入就緒狀態。 BLOCKED:當線程運行過程如果遇到了Syschronized就會進入阻塞狀態。 TIMED_WATING:表示等待狀態,有時間限制的(sleep) WAITING:表示進入一個無時間顯示的等待狀態(wait) TERMINATED:表示結束狀態。 /<code>
線程的基本操作
新建線程
實現Runable接口或者new Thread
終止線程
為什麼不建議使用stop?
因為stop方法比較暴力,強行把執行的程序終止,可能會引發數據不一致的問題,Thread.stop()方法在結束線程時,會直接終止線程,並立即釋放這個線程的所持有的鎖。比如數據寫到一半,強行終止掉了,數據就會被破壞。
線程中斷
<code>interrupt() //中斷線程。相當於設置中斷標誌位 isInterrypted() //判斷是否被中斷 interrupted() //判斷是都被中斷,並清除當前的中斷狀態 /<code>
<code> /<code>
Thread.sleep()方法由於中斷而拋出異常,此時它會清除中斷標記,如果不加處理,那麼下一次循環開始時,就無法捕獲這個異常,可以再次設置中斷標記來方便循環判斷。
等待和通知
當在一個對象實力上條用wait()方法之後。當前線程就會在這個對象上等待。在線程A中調用了obj.wait()方法,那麼變成A就會停止繼續執行,轉為等待狀態,直到其他線程調用了obj.notify()方法才有機會繼續執行(並不是一定)
如果一個線程調用了obj.wait()那麼它就會進入這個object的等待隊列,這個等待隊列可能會有多個線程,因為系統運行多個線程同時等待某一個對象,當obj.notify()方法被調用時,就會隨機選擇一個線程,將其喚醒,這個喚醒是完全隨機的。
notifyAll()方法的功能是喚醒所有在該對象等待隊列中等待的線程。
守護線程的finally不一定會執行。如果除守護線程外的所有線程都結束了,那麼守護線程就立即退出沒有執行finally代碼塊的機會。
掛起(suspend)和繼續(resume)執行
不推薦執行suspend(),因為該方法在導致線程暫停的同時,並不會釋放任何資源。影響其他想要訪問該資源的線程,直到當前線程使用resume操作,是當前線程繼續執行,但是此處有一個隱患。如果resume在suspend之前運行了,那麼被掛起的線程將不能繼續執行下去。
<code>public class BadSuspend { public static Object u = new Object(); static ChangeObjectThread t1 = new ChangeObjectThread("t1"); static ChangeObjectThread t2 = new ChangeObjectThread("t2"); public static class ChangeObjectThread extends Thread { public ChangeObjectThread(String name){ super.setName(name); } @Override public void run() { synchronized (u) { System.out.println("in "+getName()); Thread.currentThread().suspend(); } } } public static void main(String[] args) throws InterruptedException { t1.start(); Thread.sleep(100); t2.start(); t1.resume(); t2.resume();//無法直到這一行先執行,還是Thread.currentThread().suspend();先執行 t1.join(); t2.join(); } } /<code>
等待線程結束(join)和謙讓(yeild)
<code>public final synchronized void join(long millis) throws InterruptedException//有最大等待時長的等待 public final synchronized void join(long millis, int nanos) throws InterruptedException//毫秒,加納秒級別的等待 public final void join() throws InterruptedException//表示無限期的等待 /<code>
<code>package com.atmb.me; public class joinAndYeild { public volatile static int i =0; public static class myThread implements Runnable{ @Override public void run() { for (int j = 0; j < 1000000; j++) { i+=1; } } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new myThread()); thread.start(); thread.join();//等待thread線程執行結束 System.out.println(i);//結果為1000000 } } /<code>
<code> public static native void yield(); /<code>
這個方法一旦執行,就會使當前線程讓出cpu,讓出cpu之後,該線程會繼續進行cpu資源的爭奪,之後繼續正常運行。
線程組
<code>package com.atmb.me; public class ThreadGroupTest implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getThreadGroup().getName()+"==="+Thread.currentThread().getName()); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { ThreadGroup group1 = new ThreadGroup("group1"); Thread thread1 = new Thread(group1, new ThreadGroupTest()); Thread thread2 = new Thread(group1, new ThreadGroupTest()); thread1.start(); thread2.start(); System.out.println(group1.activeCount());//活躍線程數 group1.list(); } } < 2 group1===Thread-0 java.lang.ThreadGroup[name=group1,maxpri=10] group1===Thread-1 Thread[Thread-0,5,group1] Thread[Thread-1,5,group1] /<code>
守護線程(Daemon)
守護線程要守護的對象已經不存在了,那麼整個應用程序就應該技術,因此當java應用內只有守護線程時,java虛擬機就會自然退出。
<code>package geym.ch2; public class DaemonDemo { public static class DaemonT extends Thread{ public void run(){ while(true){ System.out.println("I am alive"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { Thread t=new DaemonT(); t.setDaemon(true); t.start(); // Thread.sleep(2000); } } /<code>
設置守護線程,必須要在start之前設置,否則會得到異常信息。
線程優先級
Java中的線程可以有自己的優先級。優先級高的線程在競爭資源時,會更有優勢,更大概率搶佔到資源,java中線程優先級可以設置為1-10.
其中有三個靜態標量標識三個對應的優先級。
<code> /** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10; /<code>
<code>public class PriorityDemo { public static class HightPriority extends Thread{ static int count=0; public void run(){ while(true){ synchronized(PriorityDemo.class){ count++; if(count>10000000){ System.out.println("HightPriority is complete"); break; } } } } } public static class LowPriority extends Thread{ static int count=0; public void run(){ while(true){ synchronized(PriorityDemo.class){ count++; if(count>10000000){ System.out.println("LowPriority is complete"); break; } } } } } public static void main(String[] args) throws InterruptedException { Thread high=new HightPriority(); LowPriority low=new LowPriority(); high.setPriority(Thread.MIN_PRIORITY); low.setPriority(Thread.MAX_PRIORITY); low.start(); high.start(); } } /<code>
線程安全與關鍵字synchronized
synchronized
- 指定加鎖對象,對給定的對象進行加鎖,
- 用於實例方法,對當前的實例進行加鎖
- 用於靜態方法,對當前類進行加鎖
錯誤的加鎖
<code>package com.atmb.me; public class lockError implements Runnable { public static Integer i =0; public static lockError instance = new lockError(); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(instance); Thread thread2 = new Thread(instance); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(i); } @Override public void run() { for (int j = 0; j < 100000; j++) { synchronized (i){ i+=1; } } } } < 142957 /<code>
原因在於Integer類型屬於不可變對象,一旦創建就不可能被改變,當integer對象賦值為新值時,當前引用指向的對象就改變了。
指令重排的前提
指令重排需要保證串行語義的一致性,指令重排不會使串行的語義邏輯發生問題。
指令重排的目的?
<code>為了減少中斷流水線 /<code>
那些指令不能重排
- 程序順序原則,一個線程內保證語義的串行性
- volatile規則:volatile變量的寫先與讀發生,寶整理volatile變量的可見性
- 傳遞性:解鎖必然發生在隨後的加鎖前
- 線程的start()方法先於它的動作
- 線程的所有操作先於線程的終結(interrupt)
- 線程的中斷(interrupt())先於被中斷線程的代碼
- 對象的構造函數的執行,結束先於finalize()的方法
是否會釋放鎖
- yield讓出cpu執行權,不會釋放鎖
- sleep休眠時,不會釋放鎖
- 調用wait方法之後,會釋放鎖,喚醒之後,會再次競爭鎖,然後執行wait()方法後的代碼
- nofify nodifyall 對鎖沒有影響,一般放同步代碼塊的最後一行
最後
感謝你看到這裡,看完有什麼的不懂的可以在評論區問我,覺得文章對你有幫助的話記得給我點個贊,每天都會分享java相關技術文章或行業資訊,歡迎大家關注和轉發文章!
關鍵字: new static InterruptedException