Java多線程的使用java
這裏先引用一下百度給出的概念算法
線程是系統可以進行運算調度的最小單位。他被包含在進程只中,是進程中的實際運做單位。shell
這裏只摘取簡單的引用,便於通俗的理解,畢竟百度百科上面的東西太過於專業化,專業名詞太多。微信
上面有一個名詞進程
,這裏解釋一下進程多線程
通常咱們打開一個軟件,好比qq,微信,這個時候就啓動了一個進程,在Windows任務管理器中咱們能夠看到它們
紅框部分就是一個進程jvm
好了咱們大概算是通俗的理解了什麼是進程
在當代面向線程設計的計算機結構中,進程是線程的容器。程序是指令、數據及其組織形式的描述,進程是程序的實體。ide
這裏用一個簡單的例子演示一下函數
public class test1 { private static class ther extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { try { TimeUnit.MICROSECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("啓動了一個線程"); } } } public static void main(String[] args) { ther ther = new ther(); ther.start(); for (int i = 0; i < 10; i++) { try { TimeUnit.MICROSECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主函數執行了"); } } }
咱們看一下執行結果,暫時不要理會如今的寫法學習
主函數執行了 啓動了一個線程 主函數執行了 啓動了一個線程 主函數執行了 啓動了一個線程 啓動了一個線程 主函數執行了 主函數執行了 啓動了一個線程 主函數執行了 啓動了一個線程 主函數執行了 啓動了一個線程 主函數執行了 啓動了一個線程 啓動了一個線程 主函數執行了 主函數執行了 啓動了一個線程
咱們能夠看見它是交替運行的
平時咱們的代碼都是串行的,也就是順序執行,而如今這鐘狀況就是並行的狀態,這就是線程一個比較直觀的概念操作系統
這裏列舉幾種線程經常使用的幾種方式
經過繼承Thread而後實現它的run()方法來進行建立線程
private static class exThread extends Thread{ @Override public void run() { System.out.println("繼承thread實現run方法建立線程"); } }
經過實現Runnable建立一個線程
private static class impRunnable implements Runnable{ @Override public void run() { System.out.println("經過實現Runnable建立一個線程"); } }
經過實現Callable建立一個線程,這種建立方式是有返回池的
private static class impCallable implements Callable{ @Override public Object call() throws Exception { System.out.println("經過實現Callable建立一個線程,它是有放回值的"); return "ssuccess"; } }
這裏列舉5鍾啓動線程的方式
public static void main(String[] args) { new exThread().start();//經過直接建立對象而後調用start();方法啓動一個線程 new Thread(new exThread()).start();//經過new一個線程類而後傳入須要啓動的線程,而後調用線程類的start方法啓動 new Thread(()->{ System.out.println("lambda表達式建立並啓動一個線程"); }).start(); Thread thread = new Thread(new FutureTask<String>(new impCallable())); thread.start();//經過newThread傳入FutureTask啓動 ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(()->{ System.out.println("線程池啓動並建立"); }); executorService.shutdown();//中止 }
執行結果以下
繼承thread實現run方法建立線程 繼承thread實現run方法建立線程 lambda表達式建立並啓動一個線程 經過實現Callable建立一個線程,它是有放回值的 線程池啓動並建立
稍微總結一下:
線程經常使用的建立並啓動的方式有兩種
1.經過new Thread().start();
2.經過new Thread(Runnable).start();
還有一種是使用線程池,不過本質上仍是使用上面的兩種方式
固然你也可使用lambda表達式,本質上沒什麼區別寫法不一樣而已
sleep線程休眠
這個方法頗有意思,好比說,你可在你的代碼某些業務中加個線程休眠5分鐘,而後老闆說你這個程序太慢了,你就能夠說加錢調優,還能按照百分比調整程序的運行速度。是否是很實用?
開個玩笑,開個玩笑
咱們來看下使用方式
public static void main(String[] args) { testSleep(); } static void testSleep(){ new Thread(()->{ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("我先睡一會"); }).start(); }
執行結果咱們能夠看出它過了一會才執行
線程休眠之後何時醒過來,根據sleep裏的參數決定,時間到了就醒了
線程休眠通俗理解:就是當前我不運行了,別的線程照樣能夠運行
Yield方法,翻譯過來是讓步的意思
咱們先來看一下用法而後在來解釋一下含義
static void testYield(){ new Thread(()->{ for (int i=0;i<100;i++){ System.out.println("A----"+i); if (i%10==0){ Thread.yield(); } } }).start();; new Thread(()->{ for (int i=0;i<100;i++){ System.out.println("B----"+i); if (i%10==0){ Thread.yield(); } } }).start();; }
這裏就不演示打印結果了,不是頗有表明性
講一下這個方法的做用
一個線程在執行過程當中假如調用yieid這個方法,當前線程會先中止下來進入等待隊列,這也就是讓步的意思,可是當回到等待隊列之後,在系統的調度算法裏,它依然有可能把剛回到等待隊列的線程繼續執行,固然更大的可能性仍是先執行以前等待隊列的線程,因此yieid的意思是,我雖然讓出了,可是能不能搶到執行機會我無論。
join方法,也就是加入的意思
先看寫法
Thread thread2 = new Thread(() -> { System.out.println("thread1執行了一下,如今到我執行了"); }); Thread thread1 = new Thread(() -> { System.out.println("我先執行一次,而後等下一個線程執行完在執行"); try { thread2.join(); System.out.println("thread2------執行完了,到我執行了"); } catch (InterruptedException e) { e.printStackTrace(); } }); thread1.start(); thread2.start();
在當前線程1中加入你要執行的線程2,等線程2執行完了在繼續執行線程1,這樣達到了按照順序執行線程的目的,固然,你還能夠在線程2中去join線程3,以此類推,其實就是將並行的線程串聯起來了。
當咱們在new一個線程的時候,可是尚未調用start()方法的時候,該線程處於新建狀態
線程對象調用start方法的時候,它會被線程調度器進行執行,這個時候線程就已經交給操做系統來執行了,在操做系統執行的過程當中,這整個狀態叫作Runnable
,Runnable內部又有兩個狀態,一個是Ready就緒狀態
,一個是Running運行狀態
,就緒狀態故名思意,我已經準備好了,可是還沒運行,這個時候是cpu去處理何時去運行的。當真正交給cpu去運行的時候,當前線程就處理Running狀態了。(當調用yield方法時,當前線程就會由Running狀態,變爲Ready狀態,當被cpu再次執行的時候又會進入Running狀態)
若是線程順利執行完了就會進入Teminated結束狀態
,結束之後當前線程的使命就結束了,狀態沒法回退。
在Runnable狀態還存在其餘幾種狀態,TimeWaiting等待
,Waiting等待
,Blocked阻塞
。
在同步代碼塊中,沒有獲得鎖就會進入Blocke阻塞狀態,當得到鎖之後就進入Ready就緒狀態。
在線程運行過程當中若是調用了wait(),join(),LockSupport.park()就會進入到Waiting狀態,調用notifiall(),LockSupport.unpark()就會回到Running狀態。
TimeWaiting,看名字就知道是按照時間等待,等時間到了本身會回去,Thread.sleep(time),wait(time),jion(time),LockSupport.parkNanos(),LockSupport.parkUntil()這些都是關於時間等待的方法。
上面的全部狀態均是由jvm進行管理的,jvm在使用線程的時候也要通過操做系統,jvm就是一個在操做系統上運行的程序。
線程掛起就是在當前線程執行過程當中cpu去執行另一個線程了,這屬於cpu的調度,當在執行另一個線程的時候,當前線程就是被掛起了。
這裏是我畫的一個簡陋的流程圖