我們先說一下為什么要講多線程和高并發?
原因是,你想拿到一個更高的薪水,在面試的時候呈現出了兩個方向的現象:
第一個是上天
- 項目經驗
- 高并發 緩存 大流量 大數據量的架構設計
第二個是入地
- 各種基礎算法,各種基礎的數據結構
- JVM OS 線程 IO等內容
多線程和高并發,就是入地里面的內容。
基本概念
我們先從線程的基本概念開始,給大家復習一下,不知道有多少同學是基礎不太好,說什么是線程都不知道的,如果這樣的話,花時間去補初級內容的課。
什么是叫一個進程? 什么叫一個線程?
Program app ->QQ.exe
**進程:**做一個簡單的解釋,你的硬盤上有一個簡單的程序,這個程序叫QQ.exe,這是一個程序,這個程序是一個靜態的概念,它被扔在硬盤上也沒人理他,但是當你雙擊它,彈出一個界面輸入賬號密碼登錄進去了,OK,這個時候叫做一個進程。進程相對于程序來說它是一個動態的概念
**線程:**作為一個進程里面最小的執行單元它就叫一個線程,用簡單的話講一個程序里不同的執行路徑就叫做一個線程
示例:什么叫做線程
package com.mashibing.juc.c_000;
import java.util.concurrent.TimeUnit;
public class T01_WhatIsThread { private static class T1 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("T1");}}}public static void main(String[] args) {//new T1().run();new T1().start();for(int i=0; i<10; i++) {try {TimeUnit.MICROSECONDS.sleep(1);} catch (InterruptedException e) { e.printStackTrace();}System.out.println("main");}}
}
觀察上面程序的數據結果,你會看到字符串“T1”和“Main”的交替輸出,這就是程序中有兩條不同的執行路徑在交叉執行,這就是直觀概念上的線程,概念性的東西,理解就好,沒有必要咬文嚼字的去背文字的定義。
創建線程的幾種方式
package com.mashibing.juc.c_000;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;public class T02_HowToCreateThread {static class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello MyThread!");}
}static class MyRun implements Runnable {@Overridepublic void run() {System.out.println("Hello MyRun!");}
}static class MyCall implements Callable<String> {@Overridepublic String call() {System.out.println("Hello MyCall"); return "success";}
}//啟動線程的5種方式public static void main(String[] args) {new MyThread().start();new Thread(new MyRun()).start(); new Thread(()->{System.out.println("Hello Lambda!");}).start();Thread t = new Thread(new FutureTask<String>(new MyCall()));t.start();ExecutorService service = Executors.newCachedThreadPool(); service.execute(()->{System.out.println("Hello ThreadPool");});service.shutdown();
}}
分享一道面試題
請你告訴我啟動線程的三種方式 ?
你說第一個:new Thread().start(); 第二個: new Thread(Runnable).start() 這沒問題 ;那第三個呢,要回答線程池也是用的這兩種之一,他這么問有些吹毛求疵的意思,你就可以說通過線程池也可以啟動一個新的線程 3:Executors.newCachedThreadPool()或者FutureTask + Callable
我們來認識幾個線程的方法
package com.mashibing.juc.c_000;
public class T03_Sleep_Yield_Join { public static void main(String[] args) { //testSleep(); //testYield(); testJoin();} /*Sleep,**意思就是睡眠,當前線程暫停一段時間讓給別的線程去運行。**Sleep**是怎么復活的?由你的睡眠時間而定,等睡眠到規定的時間自動復活***/ static void testSleep() { new Thread(()->{ for(int i=0; i<100; i++) { System.out.println("A" + i); try { Thread.sleep(500);//TimeUnit.Milliseconds.sleep(500)} catch (InterruptedException e) { e.printStackTrace();}}}).start();}/*Yield,就是當前線程正在執行的時候停止下來進入等待隊列,回到等待隊列里在系統的調度算法里頭呢還是依然有可能把你剛回去的這個線程拿回來繼續執行,當然,更大的可能性是把原來等待的那些拿出一個來執行,所以yield的意思是我讓出一下CPU,后面你們能不能搶到那我不管*/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();
}/*join, 意思就是在自己當前線程加入你調用Join的線程(),本線程等待。等調用的線程運行完了,自己再去執行。t1和t2兩個線程,在t1的某個點上調用了t2.join,它會跑到t2去運行,t1等待t2運行完畢繼續t1運行(自己join自己沒有意義) */static void testJoin() {Thread t1 = new Thread(()->{for(int i=0; i<100; i++) {System.out.println("A" + i);try {Thread.sleep(500);//TimeUnit.Milliseconds.sleep(500)} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2 = new Thread(()->{try {t1.join();} catch (InterruptedException e) { e.printStackTrace();}for(int i=0; i<100; i++) {System.out.println("A" + i);try {Thread.sleep(500);//TimeUnit.Milliseconds.sleep(500)} catch (InterruptedException e) { e.printStackTrace();}}});t1.start();t2.start();}
}
線程狀態
常見的線程狀態有六種:
當我們new一個線程時,還沒有調用start()該線程處于新建狀態
線程對象調用 start()方法時候,他會被線程調度器來執行,也就是交給操作系統來執行了,那么操作系統來執行的時候,這整個的狀態叫Runnable,Runnable內部有兩個狀態**(1)Ready就緒狀態/(2)Running運行狀態**。就緒狀態是說扔到CPU的等待隊列里面去排隊等待CPU運行,等真正扔到CPU上去運行的時候才叫Running運行狀態。(調用yiled時候會從Running狀態跑到Ready狀態去,線程配調度器選中執行的時候又從Ready狀態跑到Running狀態去)
如果你線程順利的執行完了就會進去**(3)Teminated****結束狀態**,(需要注意Teminated完了之后還可不可以回到new狀態再調用start?這是不行的,完了這就是結束了)
在Runnable這個狀態里頭還有其他一些狀態的變遷**(4)TimedWaiting等待、(5)Waiting等待、(6)Blocked阻塞**,在同步代碼塊的情況就下沒得到鎖就會阻塞狀態,獲得鎖的時候是就緒狀態運行。在運行的時候如果調用了o.wait()、t.join()、LockSupport.park()進入Waiting狀態,調用o.notify()、o.notifiAll()、LockSupport.unpark()就又回到Running狀態。TimedWaiting按照時間等待,等時間結束自己就回去了,Thread.sleep(time)、o.wait(time)、t.jion(time)、LockSupport.parkNanos()、LockSupport.parkUntil()這些都是關于時間等待的方法。
問題1:哪些是JVM管理的?哪些是操作系統管理的?
上面這些狀態全是由JVM管理的,因為JVM管理的時候也要通過操作系統,所以呢,那個是操作系統和那個是JVM他倆分不開,JVM是跑在操作系統上的一個普通程序 。
問題2:線程什么狀態時候會被掛起?掛起是否也是一個狀態?
Running的時候,在一個cpu上會跑很多個線程,cpu會隔一段時間執行這個線程一下,在隔一段時間執行那個線程一下,這個是cpu內部的一個調度,把這個狀態線程扔出去,從running扔回去就叫線程被掛起,cpu控制它。
來看一下ThraedState這段代碼。
package com.mashibing.juc.c_000;
public class T04_ThreadState {static class MyThread extends Thread {@Overridepublic void run() {System.out.println(this.getState());for(int i=0; i<10; i++) {try {Thread.sleep(500);} catch (InterruptedException e) { e.printStackTrace();}System.out.println(i);}}}public static void main(String[] args) {Thread t = new MyThread();//怎么樣得到這個線程的狀態呢?就是通過getState()這個方法System.out.println(t.getState());//他是一個new狀態t.start();//到這start完了之后呢是Runnable的狀態try {t.join();} catch (InterruptedException e) { e.printStackTrace();}//然后join之后,結束了是一個Timenated狀態System.out.println(t.getState());}
}