一。多線程建立方式算法
1。第一種繼承Thread類 重寫run方法編程
class CreateThread extends Thread { // run方法中,須要線程須要執行代碼 @Override public void run() { System.out.println("運行子線程"); } } public class ThreadDemo01 { public static void main(String[] args) { System.out.println("main... 主線程開始..."); // 1.建立線程 CreateThread CreateThread = new CreateThread(); // 2.啓動線程 CreateThread.start(); System.out.println("main... 主線程結束..."); } }
2。 第二種實現Runnable接口,重寫run方法多線程
class CreateRunnable implements Runnable { @Override public void run() { System.out.println("運行子線程"); } } public class ThreadDemo2 { public static void main(String[] args) { System.out.println("-----多線程建立開始-----"); // 1.建立一個線程 CreateRunnable createThread = new CreateRunnable(); // 2.開始執行線程 注意 開啓線程不是調用run方法,而是start方法 System.out.println("-----多線程建立啓動-----"); Thread thread = new Thread(createThread); thread.start(); System.out.println("-----多線程建立結束-----"); } }
3。使用匿名內部類方式併發
public class ThreadDemo3 { public static void main(String[] args) { System.out.println("-----多線程建立開始-----"); Thread thread = new Thread(new Runnable() { public void run() { System.out.println("運行子線程"); } }); thread.start(); System.out.println("-----多線程建立結束-----"); } }
注意:ide
1> 通常使用Runnable接口,符合面向接口編程,並且實現了接口還能夠繼續繼承,繼承了類不能再繼承。函數
2> 開啓線程不是調用run方法,而是start方法。優化
二。守護線程this
Java中有兩種線程,一種是用戶線程,另外一種是守護線程。spa
用戶線程是指用戶自定義建立的線程,主線程中止,用戶線程不會中止。線程
守護線程當進程不存在或主線程中止,守護線程也會被中止。
使用setDaemon(true)方法設置爲守護線程。若是不設置爲守護線程,子線程還好運行。
public class DaemonThread { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (Exception e) { // TODO: handle exception } System.out.println("我是子線程..."); } } }); thread.setDaemon(true); thread.start(); for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (Exception e) { } System.out.println("我是主線程"); } System.out.println("主線程執行完畢!"); } }
三。多線程運行狀態
線程從建立、運行到結束老是處於下面五個狀態之一:新建狀態、就緒狀態、運行狀態、阻塞狀態及死亡狀態。
1。新建狀態
當用new操做符建立一個線程時, 例如new Thread(r),線程尚未開始運行,此時線程處在新建狀態。 當一個線程處於新生狀態時,程序尚未開始運行線程中的代碼。
2。就緒狀態
當線程對象調用start()方法即啓動了線程,start()方法建立線程運行的系統資源,並調度線程運行run()方法。當start()方法返回後,線程就處於就緒狀態。
處於就緒狀態的線程並不必定當即運行run()方法,線程還必須同其餘線程競爭CPU時間,只有得到CPU時間才能夠運行線程。
3。運行狀態
當線程得到CPU時間後,它才進入運行狀態,真正開始執行run()方法。
4。阻塞狀態
線程運行過程當中,可能因爲各類緣由進入阻塞狀態:
1> 線程經過調用sleep方法進入睡眠狀態;
2> 線程調用一個在I/O上被阻塞的操做,即該操做在輸入輸出操做完成以前不會返回到它的調用者;
3> 線程試圖獲得一個鎖,而該鎖正被其餘線程持有;
4> 線程在等待某個觸發條件;
5。死亡狀態
有兩個緣由會致使線程死亡:
1> run方法正常退出而天然死亡,
2> 一個未捕獲的異常終止了run方法而使線程猝死。
爲了肯定線程在當前是否存活着(就是要麼是可運行的,要麼是被阻塞了),須要使用isAlive方法。若是是可運行或被阻塞,這個方法返回true;
若是線程仍舊是new狀態且不是可運行的, 或者線程死亡了,則返回false。
四。join()方法做用
當在主線程當中執行到t1.join()方法時,就認爲主線程應該把執行權讓給t1,執行完子線程後才執行主線程。
public class Main { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("先執行子線程"); } }); t1.start(); // 當在主線程當中執行到t1.join()方法時,就認爲主線程應該把執行權讓給t1 t1.join(); System.out.println("接着執行主線程"); } }
2。優先級
在JAVA線程中,經過一個int priority來控制優先級,範圍爲1-10,其中10最高,默認值爲5。優先級越高,被分配執行的機率就越大。
class PrioritytThread implements Runnable { public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().toString() + "---i:" + i); } } } public class ThreadDemo4 { public static void main(String[] args) { PrioritytThread prioritytThread = new PrioritytThread(); Thread t1 = new Thread(prioritytThread); Thread t2 = new Thread(prioritytThread); t1.start(); // 注意設置了優先級, 不表明每次都必定會被執行。 只是被CPU調度機率高。 t1.setPriority(10); t2.start(); } }
3。Yield方法
yield()讓當前正在運行的線程回到可運行狀態,以容許具備相同優先級的其餘線程得到運行的機會。但可能會沒有效果,由於該線程可能會再次被選中運行。
五。內置的鎖
Java提供了一種內置的鎖機制來支持原子性,每個Java對象均可以用做一個實現同步的鎖,稱爲內置鎖。
線程進入同步代碼塊以前自動獲取到鎖,代碼塊執行完成正常退出或代碼塊中拋出異常退出時會釋放掉鎖。
內置鎖爲互斥鎖,即線程A獲取到鎖後,線程B阻塞直到線程A釋放鎖,線程B才能獲取到同一個鎖。
內置鎖使用synchronized關鍵字實現,synchronized關鍵字有兩種用法:
*** 修飾須要進行同步的方法(全部訪問狀態變量的方法都必須進行同步),此時充當鎖的對象爲調用同步方法的對象。
*** 同步代碼塊和直接使用synchronized修飾須要同步的方法是同樣的,可是鎖的粒度能夠更細,而且充當鎖的對象不必定是this,也能夠是其它對象,因此使用起來更加靈活。
class SynObj{ // 同步方法,鎖對象爲this public synchronized void showA(){ System.out.println("showA.."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } public void showB(){ // 同步代碼塊,鎖對象爲this synchronized (this) { System.out.println("showB.."); } } public void showC(){ String s="1"; // 同步代碼塊,鎖對象爲s synchronized (s) { System.out.println("showC.."); } } // 靜態同步函數,鎖是該函數所屬字節碼文件對象,可使用this.getClass()方法獲取,也能夠用當前 類名.class表示。 public static synchronized void showD(){ System.out.println("showD.."); } } public class TestSyn { public static void main(String[] args) { final SynObj sy=new SynObj(); new Thread(new Runnable() { @Override public void run() { sy.showA(); } }).start(); new Thread(new Runnable() { @Override public void run() { sy.showB(); } }).start(); new Thread(new Runnable() { @Override public void run() { sy.showC(); } }).start(); new Thread(new Runnable() { @Override public void run() { SynObj.showD(); } }).start(); } }
shouA和showC以及showD會馬上打印出來,showC三秒後纔打印處理,由於showA和showB的鎖對象都是this。
2。多線程死鎖
多個併發進程因爭奪系統資源而產生相互等待的現象。
死鎖產生的4個必要條件:
1> 互斥:某種資源一次只容許一個進程訪問,即該資源一旦分配給某個進程,其餘進程就不能再訪問,直到該進程訪問結束。
2> 佔有且等待:一個進程自己佔有資源(一種或多種),同時還有資源未獲得知足,正在等待其餘進程釋放該資源。
3> 不可搶佔:別人已經佔有了某項資源,你不能由於本身也須要該資源,就去把別人的資源搶過來。
4> 循環等待:存在一個進程鏈,使得每一個進程都佔有下一個進程所需的至少一種資源。
避免死鎖:銀行家算法等
六。ThreadLocal
ThreadLocal爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本。
ThreadLocal類接口很簡單,只有4個方法:
1。void set(Object value)設置當前線程的線程局部變量的值。
2。public Object get()該方法返回當前線程所對應的線程局部變量。
3。public void remove()將當前線程局部變量的值刪除,目的是爲了減小內存的佔用。
當線程結束後,對應該線程的局部變量將自動被垃圾回收,因此顯式調用該方法清除線程的局部變量並非必須的操做,但它能夠加快內存回收的速度。
4。protected Object initialValue()返回該線程局部變量的初始值。
這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,而且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。
class Res { public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() { protected Integer initialValue() { return 0; }; }; public Integer getNumber() { int count = threadLocal.get() + 1; threadLocal.set(count); return count; } } public class ThreadLocaDemo2 extends Thread{ private Res res; public ThreadLocaDemo2(Res res) { this.res = res; } @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "," + res.getNumber()); } } public static void main(String[] args) { Res res = new Res(); ThreadLocaDemo2 t1 = new ThreadLocaDemo2(res); ThreadLocaDemo2 t2 = new ThreadLocaDemo2(res); t1.start(); t2.start(); } }
ThreadLoca實現原理:
每一個Thread的對象都有一個ThreadLocalMap,當建立一個ThreadLocal的時候,就會將該ThreadLocal對象添加到該Map中,其中鍵就是ThreadLocal,值能夠是任意類型。
七。多線程三大特性
1。原子性:一個操做或者多個操做 要麼所有執行而且執行的過程不會被任何因素打斷,要麼就都不執行。
2。可見性:當多個線程訪問同一個變量時,一個線程修改了這個變量的值,其餘線程可以當即看獲得修改的值。
3。有序性:程序執行的順序按照代碼的前後順序執行。
通常來講處理器爲了提升程序運行效率,可能會對輸入代碼進行優化。
它不保證程序中各個語句的執行前後順序同代碼中的順序一致,可是它會保證程序最終執行結果和代碼順序執行的結果是一致的。