1? ? ? 多線程--初步
【
1進程
比如:QQ、迅雷、360、飛秋...
2線程的概念
3線程的意義:
4.Java程序的運行原理
代碼是運行在線程中的,如果一個進程沒有線程,那么進程就結束了,也就是說一個進程至少要有一個線程
當開啟一個Java程序之后,在進程中至少會自動創建兩個線程:主線程、垃圾回收線程
實際上,使用多線程并不是為了提高程序運行的速度,而是為了提高CPU的使用率
多線程:在進程中同時執行著多個執行路徑,并且多個線程(執行路徑)之間互不干擾
使用場景:
迅雷下載、360多個任務的執行、QQ視屏和文件傳輸………
】
【
for(int i=10;i<=100;i+=10){
System.out.println("洗衣服,進度"+i+"%");
Thread.sleep(500);
}
for(int i=0;i<=100;i+=10){
System.out.println("做飯,進度"+i+"%");
Thread.sleep(500);
}
//創建
WashThread wash = newWashThread();
CookThread cook = newCookThread();
//啟動
wash.start();
cook.start();
for(inti=10;i<=100;i+=10){
System.out.println("微波爐加熱,進度"+i+"%");
try {
Thread.sleep(500);
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
//每隔10秒打印Hello,同時可以接受用戶輸入,并打印出來
Scanner s = newScanner(System.in);
PrintThread t = newPrintThread();
t.start();
while(true){
String line =s.nextLine();
System.out.println(line);
}
//選擇性學習
LagRunnable r = newLagRunnable();
ExecutorService service =Executors.newCachedThreadPool();
service.execute(r);
service.execute(r);
service.execute(r);
service.shutdown();
//**************************************
FileReader fr = newFileReader("g:/config.txt");
BufferedReader br = newBufferedReader(fr);
//?????????????? ReverseRunnable r =newReverseRunnable();
String name = br.readLine();
Class clz =Class.forName(name);
Runnable r=(Runnable)clz.newInstance();
//**************************************
//以上部分選擇性學習,擴展內容
Thread t = new Thread(r);
t.start();
】
進程運行起來的程序
線程可以理解為一個子進程
進程間進行切換的代價較大,不能共享資源
線程間切換代價小,可以共享數據和邏輯
Java進程每運行一個java應用程序,就會啟動一個虛擬機進程,我們的程序就在其中運行
Java線程每個java進程中都默認有一個主線程,名字為main
(我們之前的程序都是在主線程中執行)
Thread類java中的線程,在一個線程中的代碼邏輯就像被一根線串起來一樣,從前往后順序執行的
,節省時間、避免程序阻塞帶來的問題
分時復用多個線程不停地快速切換使用CPU,表面上看是同時執行,其實也是有先后順序的,同一時間只有一個線程占有CPU。
創建-->就緒-->運行-->結束
運行-->阻塞--〉就緒
【
假如當前計算機只有一個CPU且只有一個核心,那么CPU在某一個時刻只能執行一條指令,線程只有得到CPU時間片,也就是使用權,才可以執行指令,那么Java是如何對線程進行調用的呢?
】
搶占調度模型:優先讓優先級高的線程使用CPU,如果線程的優先級相同,那么會隨機選擇一個,優先級高的線程獲取CPU時間片相對多一些
Java使用的是搶占式調度模型
優先級:獲取CPU資源的幾率
優先級越高的線程會獲取越多的運行機會
優先級的取值范圍是1-10之間的整數
Java將差別最大的三個優先級定義成了Thread類中的靜態常量
MAX_PRIORITY:最高優先級(10)
MIN_PRIORITY:最低優先級(1)
NORM_PRIORITY:默認優先級(5)
可以通過setPriority()和getPriority()方法進行設置和獲取
【
線程的創建和使用
在Java中一切皆對象,線程這一類事物也用了一個類來進行了描述,這個類叫做Thread類
線程的創建
1、繼承Thread類
2、實現Runnable接口
1、 通過繼承Thread類會出現單繼承的問題,通過實現Runnable接口可以避免單繼承的不足
2、 通過實現Runnable接口可以將線程的任務從線程的子類中分離出來,進行單獨的封裝,按照面向對象的思想將任務封裝成了對象,并且可以方便實現多個線程的數據共享
】
1、定義一個類繼承Thread,重寫父類的run()方法,在run()中寫上要在子線程中執行的邏輯
2、創建定義好的類,生成一個實例
3、調用這個實例的start(),啟動線程(不要調用run(),這個run()是一個回調方法)
1、定義一個類實現Runnable接口,實現抽象方法run(),在run()中寫上要在子線程中執行的邏輯
2、創建一個Thread實例,從構造方法參數傳入1中定義類的實例
3、調用start()
Runnable方式從設計上耦合度更低,更靈活
【
@Override
public void run() {
int progress = 0;? //初始化為0
while(true){
System.out.println(Thread.currentThread().getName()
+"燒飯,進度"+progress+"%");
if(progress ==100){//進度到100停止循環
break;
}
try {
Thread.sleep(100);
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
progress++;?? //每次進度+1
}
}
Threadt1 = new Thread(new CookRunnable());
t1.start();
Thread t2 = new Thread(newCookRunnable());
t2.start();
CookRunnabler=newCookRunnable();
Threadt1=newThread(r);//兩個線程使用同一個Runnable對象
Threadt2=newThread(r);
//以下方式不能共享,因為是分別使用了兩個Runnable對象的兩個progress屬性
/* Thread t1 = new Thread(newCookRunnable());
Thread t2 = new Thread(newCookRunnable());*/
t1.start();//啟動
try{
Thread.sleep(50);//錯開50毫秒
}catch(InterruptedExceptione) {
//TODOAuto-generated catch block
e.printStackTrace();
}
t2.start();//啟動另一個線程
}
】
1、局部變量不能被多個線程共享
2、成員變量可以被線程共享
【
Thread t = new Thread(new Runnable() {
@Override
publicvoid run() {
System.out.println("開始睡眠");
try{
Thread.sleep(20000);//睡眠20秒
}catch (InterruptedException e) {
//TODO Auto-generated catch block
System.out.println("睡到一半被斷了");
}
System.out.println("結束睡眠");
}
});
t.start();
try{
Thread.sleep(5000);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
t.interrupt();//啟動子線程5秒后打斷它的睡眠
}
】
線程的優先級:
【
t2.setPriority(10);
t1.start();
t2.start();
】
Stop方法讓線程停下:
【
public class CycleRunnable implements Runnable{
privateboolean control = true;
publicvoid setControl(boolean control) {
this.control= control;
}
@Override
publicvoid run() {
while(control){
try{
Thread.sleep(3000);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
publicstatic void main(String[] args) {
CycleRunnabler =new CycleRunnable();
Threadt = new Thread(r);
t.setName("停不下來的線程");//設置線程名字
t.start();
Scanners = new Scanner(System.in);
while(true){
Stringline = s.nextLine();
System.out.println("line="+line);
if(line.equals("stop")){
r.setControl(false);
break;
}
}
}
】
禮讓Yield():
【
Thread t2 = new Thread(new Runnable() {
@Override
publicvoid run() {
for(inti=1;i<=1000;i++){
System.out.println("????? "+i);
Thread.yield();//讓給別的線程使用CPU
//讓當前正在執行的線程釋放CPU資源,讓其他線程有機會去搶占CPU資源
//有可能在釋放CPU資源之后,立馬自己又再搶到了CPU資源
}
}
});
】
Thread.currentThread()通過Thread的靜態方法獲得當前線程的對象,還可以調用這個對象的getName()方法獲取其名字
Thread.sleep(longm)通過這個靜態方法,可以睡眠m毫秒,當前線程睡眠
setName(Stringn)設置線程名
intgetPriority()獲取優先級,數字越大優先級越高,優先級高則搶占CPU成功的概率高一些,優先級范圍1-10,默認5
setPriority(intp)設置優先級
yield()讓出CPU使用權,但可以馬上再搶回來
interrupt()方法,能打斷一個線程sleep()、wait()、join()等方法,能讓這些方法拋出InterruptedException
join()會合,等待子線程直到它結束,加入當前正在運行的線程,等待加入的線程執行完,再繼續執行被加入的線程
setDaemon(boolean)方法將指定的線程設置為后臺線程,設置守護線程,所有前臺線程都結束后,后臺線程(守護線程)會自動結束,該方法要在start()方法之前調用
線程加入:
【
public class ThreadJoinDemo{
public static void main(String[] args) throwsInterruptedException {
//線程加入
ThreadJoin t1 = new ThreadJoin("線程A");
ThreadJoin t2 = new ThreadJoin("線程B");
ThreadJoin t3 = new ThreadJoin("線程C");
t1.start();
/**
* 加入線程,讓指定的線程(t1)加入到當前正在運行的線程(主線程)中
* 并且保證指定的線程執行完之后,在繼續執行當前線程
*/
t2.start();
t1.join();
t3.start();
}
}
class ThreadJoin extends Thread{
public ThreadJoin(String name){
super(name);
}
public void run(){
for(int i=0;i<10;i++){
try {
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"......."+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
】
守護線程(后臺線程):
【
public classThreadDaemonDemo {
public static void main(String[] args) throwsInterruptedException {
// 設置守護線程
ThreadDaemon t1 = new ThreadDaemon("線程A");
ThreadDaemon t2 = new ThreadDaemon("線程B");
ThreadDaemon t3 = new ThreadDaemon("線程C");
t1.start();
Thread.sleep(5000);
/*
* 設置守護線程(后臺線程)
* 開啟的線程默認都是前臺線程,可以通過setDaemon(boolean)方法將指定線程設置為后臺線程,
* 當所有前臺線程都結束之后,后臺線程會自動結束(無論有沒有執行完)
*
* 現在,t1和主線程是前臺線程,而t2和t3被設置為后臺線程,也就是說當t1和主線程都結束之后,
* 也就代表前臺線程全部結束了,那么兩個后臺線程(t2和t3)會自動結束
*/
t2.setDaemon(true);
t3.setDaemon(true);
t2.start();
t3.start();
}
}
class ThreadDaemon extendsThread {
public ThreadDaemon(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
try {
Thread.sleep(500);
System.out.println(getName() +"..." +i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
】
線程停止:
【
public class ThreadStop {
public static void main(String[] args) throwsInterruptedException {
Stops t1 = new Stops("線程A");
t1.start();
// 3秒之后停止子線程運行
Thread.sleep(3000);
// 1)使用stop方法
// t1.stop();
// 2)設置標記
// 停止線程最本質的原理其實就是讓run()方法執行完
// t1.setFlag(false);
// 3)使用interrupt方法
t1.interrupt();
// System.out.println("結束線程");
}
}
class Stops extends Thread{
private boolean runFlag = true;
public Stops(String name){
super(name);
}
@Override
public void run() {
// while(flag) {
while(!isInterrupted()) { // 判斷中斷標記是否為true
System.out.println("子線程...");
try {
/*
* 阻塞狀態會被中斷標記終止,如果是被強制終止阻塞狀態的話會拋出中斷異常(InterruptedException)
* 并且在結束阻塞狀態之后,會清除中斷標記(也就是說isInterrupted()返回值為false)
*/
Thread.sleep(6000);
} catch (InterruptedException e) {
//打印異常信息
// e.printStackTrace();
break;
}
}
System.out.println("線程已經運行完畢...");
}
public? voidsetFlag(boolean flag){
this.runFlag = flag;
}
}
】
1.stop()方法,不建議使用,要讓線程結束,就讓它執行完;可以使用一個條件變量作為線程中循環條件,然后在外界通過控制這個變量,來使得線程結束(終止線程,已被廢棄,不推薦使用)
2、設置標記
3.interrupt():中斷線程的阻塞狀態,調用該方法之后,線程會標記一個中斷標記,當線程處于阻塞(比如休眠) 狀態時,如果線程具備中斷狀態,就會直接中斷當前的阻塞狀態,并去除中斷標記
(isInterrupted():測試線程是否已經中斷)
PS:停止線程最本質的原理其實就是讓run()方法執行完
生命周期:生命周期:從創建到結束的整個過程,在這期間會包含很多階段(狀態)
新建狀態(New):MyThread??t1 = new MyThread();
就緒狀態(Runnable):調用start()方法,使線程具有了運行資格,但是沒有CPU的執行權(就緒隊列:CPU在分配執行權的時候是在就緒隊列中隨機和挑選一個線程并分配其執行權(CPU調度)),等待CPU調度
運行狀態(Running):獲取了CPU的執行權,只有在運行狀態下的線程才具備CPU的執行權
阻塞狀態(Blocked):在運行狀態下調用了sleep、wait或其他的一切方法使線程進入阻塞狀態(睡眠池、等待池)
【sleep(毫秒值)《睡眠池:一塊內存池,集合對象或數組》?? wait()《等待池》】
《睡眠結束(時間結束或被中斷)被喚醒(notify) -à>>>會再次進入就緒狀態》
死亡狀態(Dead):stop()??? run()執行完《線程一旦結束進入死亡之后,就沒有用了,該狀態是不可逆的》