1? ? ? 多線程--初步
【
1進程
比如:QQ、迅雷、360囤攀、飛秋...
注意:操作系統(tǒng)中安裝了很多應(yīng)用程序,只有當開啟之后宫纬,系統(tǒng)才會為這個程序分配系統(tǒng)資源(CPU執(zhí)行權(quán)焚挠、內(nèi)存...)讓其運行在系統(tǒng)中
2線程的概念
3線程的意義:
每當一個進程開啟之后,系統(tǒng)會為這個一個進程分配系統(tǒng)資源漓骚,而線程是進程中的一部分蝌衔,當開啟一個線程之后,系統(tǒng)不會單獨為其分配內(nèi)存蝌蹂,而是一個進程中的多個線程共享它們所在的進程的資源噩斟,所以線程也可以理解為是輕量級的進程。
4.Java程序的運行原理
代碼是運行在線程中的孤个,如果一個進程沒有線程剃允,那么進程就結(jié)束了,也就是說一個進程至少要有一個線程
當開啟一個Java程序之后硼身,在進程中至少會自動創(chuàng)建兩個線程:主線程硅急、垃圾回收線程
實際上,使用多線程并不是為了提高程序運行的速度佳遂,而是為了提高CPU的使用率
多進程:在系統(tǒng)中同時運行著多個應(yīng)用程序营袜,并且多個進程(應(yīng)用程序)之間互不干擾
多線程:在進程中同時執(zhí)行著多個執(zhí)行路徑,并且多個線程(執(zhí)行路徑)之間互不干擾
使用場景:
迅雷下載丑罪、360多個任務(wù)的執(zhí)行荚板、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);
}
//創(chuàng)建
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();
//**************************************
//以上部分選擇性學習,擴展內(nèi)容
Thread t = new Thread(r);
t.start();
】
進程運行起來的程序
線程可以理解為一個子進程
進程間進行切換的代價較大煤搜,不能共享資源
線程間切換代價小免绿,可以共享數(shù)據(jù)和邏輯
Java進程每運行一個java應(yīng)用程序,就會啟動一個虛擬機進程擦盾,我們的程序就在其中運行
Java線程每個java進程中都默認有一個主線程嘲驾,名字為main
(我們之前的程序都是在主線程中執(zhí)行)
Thread類java中的線程,在一個線程中的代碼邏輯就像被一根線串起來一樣迹卢,從前往后順序執(zhí)行的
多線程辽故,創(chuàng)建主線程以外的子線程,讓多段邏輯"同時"執(zhí)行
腐碱,節(jié)省時間誊垢、避免程序阻塞帶來的問題
分時復用多個線程不停地快速切換使用CPU,表面上看是同時執(zhí)行,其實也是有先后順序的喂走,同一時間只有一個線程占有CPU殃饿。
創(chuàng)建-->就緒-->運行-->結(jié)束
運行-->阻塞--〉就緒
【
假如當前計算機只有一個CPU且只有一個核心,那么CPU在某一個時刻只能執(zhí)行一條指令缴啡,線程只有得到CPU時間片壁晒,也就是使用權(quán)瓷们,才可以執(zhí)行指令业栅,那么Java是如何對線程進行調(diào)用的呢?
】
分時調(diào)度模型:所有線程輪流使用CPU的使用權(quán)谬晕,平均分配每個線程占用CPU的時間片
搶占調(diào)度模型:優(yōu)先讓優(yōu)先級高的線程使用CPU碘裕,如果線程的優(yōu)先級相同,那么會隨機選擇一個攒钳,優(yōu)先級高的線程獲取CPU時間片相對多一些
Java使用的是搶占式調(diào)度模型
優(yōu)先級:獲取CPU資源的幾率
優(yōu)先級越高的線程會獲取越多的運行機會
優(yōu)先級的取值范圍是1-10之間的整數(shù)
Java將差別最大的三個優(yōu)先級定義成了Thread類中的靜態(tài)常量
MAX_PRIORITY:最高優(yōu)先級(10)
MIN_PRIORITY:最低優(yōu)先級(1)
NORM_PRIORITY:默認優(yōu)先級(5)
可以通過setPriority()和getPriority()方法進行設(shè)置和獲取
【
線程的創(chuàng)建和使用
在Java中一切皆對象帮孔,線程這一類事物也用了一個類來進行了描述,這個類叫做Thread類
線程的創(chuàng)建
1不撑、繼承Thread類
2文兢、實現(xiàn)Runnable接口
1焕檬、 通過繼承Thread類會出現(xiàn)單繼承的問題姆坚,通過實現(xiàn)Runnable接口可以避免單繼承的不足
2、 通過實現(xiàn)Runnable接口可以將線程的任務(wù)從線程的子類中分離出來实愚,進行單獨的封裝兼呵,按照面向?qū)ο蟮乃枷雽⑷蝿?wù)封裝成了對象,并且可以方便實現(xiàn)多個線程的數(shù)據(jù)共享
】
一腊敲、使用Thread繼承創(chuàng)建
1击喂、定義一個類繼承Thread,重寫父類的run()方法,在run()中寫上要在子線程中執(zhí)行的邏輯
2碰辅、創(chuàng)建定義好的類懂昂,生成一個實例
3、調(diào)用這個實例的start(),啟動線程(不要調(diào)用run(),這個run()是一個回調(diào)方法)
1凌彬、定義一個類實現(xiàn)Runnable接口,實現(xiàn)抽象方法run()榕吼,在run()中寫上要在子線程中執(zhí)行的邏輯
2饿序、創(chuàng)建一個Thread實例,從構(gòu)造方法參數(shù)傳入1中定義類的實例
3羹蚣、調(diào)用start()
Runnable方式從設(shè)計上耦合度更低原探,更靈活
【
@Override
public void run() {
int progress = 0;? //初始化為0
while(true){
System.out.println(Thread.currentThread().getName()
+"燒飯,進度"+progress+"%");
if(progress ==100){//進度到100停止循環(huán)
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徒蟆、成員變量可以被線程共享
interrupt()方法:
【
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("結(jié)束睡眠");
}
});
t.start();
try{
Thread.sleep(5000);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
t.interrupt();//啟動子線程5秒后打斷它的睡眠
}
】
線程的優(yōu)先級:
【
t1.setPriority(1);
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("停不下來的線程");//設(shè)置線程名字
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
//讓當前正在執(zhí)行的線程釋放CPU資源,讓其他線程有機會去搶占CPU資源
//有可能在釋放CPU資源之后型型,立馬自己又再搶到了CPU資源
}
}
});
】
Thread.currentThread()通過Thread的靜態(tài)方法獲得當前線程的對象段审,還可以調(diào)用這個對象的getName()方法獲取其名字
Thread.sleep(longm)通過這個靜態(tài)方法,可以睡眠m毫秒闹蒜,當前線程睡眠
setName(Stringn)設(shè)置線程名
intgetPriority()獲取優(yōu)先級寺枉,數(shù)字越大優(yōu)先級越高,優(yōu)先級高則搶占CPU成功的概率高一些绷落,優(yōu)先級范圍1-10姥闪,默認5
setPriority(intp)設(shè)置優(yōu)先級
yield()讓出CPU使用權(quán),但可以馬上再搶回來
interrupt()方法砌烁,能打斷一個線程sleep()筐喳、wait()、join()等方法函喉,能讓這些方法拋出InterruptedException
join()會合避归,等待子線程直到它結(jié)束,加入當前正在運行的線程管呵,等待加入的線程執(zhí)行完梳毙,再繼續(xù)執(zhí)行被加入的線程
setDaemon(boolean)方法將指定的線程設(shè)置為后臺線程,設(shè)置守護線程撇寞,所有前臺線程都結(jié)束后顿天,后臺線程(守護線程)會自動結(jié)束,該方法要在start()方法之前調(diào)用
線程加入:
【
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)加入到當前正在運行的線程(主線程)中
* 并且保證指定的線程執(zhí)行完之后牌废,在繼續(xù)執(zhí)行當前線程
*/
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 {
// 設(shè)置守護線程
ThreadDaemon t1 = new ThreadDaemon("線程A");
ThreadDaemon t2 = new ThreadDaemon("線程B");
ThreadDaemon t3 = new ThreadDaemon("線程C");
t1.start();
Thread.sleep(5000);
/*
* 設(shè)置守護線程(后臺線程)
* 開啟的線程默認都是前臺線程,可以通過setDaemon(boolean)方法將指定線程設(shè)置為后臺線程啤握,
* 當所有前臺線程都結(jié)束之后鸟缕,后臺線程會自動結(jié)束(無論有沒有執(zhí)行完)
*
* 現(xiàn)在,t1和主線程是前臺線程排抬,而t2和t3被設(shè)置為后臺線程懂从,也就是說當t1和主線程都結(jié)束之后,
* 也就代表前臺線程全部結(jié)束了蹲蒲,那么兩個后臺線程(t2和t3)會自動結(jié)束
*/
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)設(shè)置標記
// 停止線程最本質(zhì)的原理其實就是讓run()方法執(zhí)行完
// t1.setFlag(false);
// 3)使用interrupt方法
t1.interrupt();
// System.out.println("結(jié)束線程");
}
}
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 {
/*
* 阻塞狀態(tài)會被中斷標記終止番甩,如果是被強制終止阻塞狀態(tài)的話會拋出中斷異常(InterruptedException)
* 并且在結(jié)束阻塞狀態(tài)之后,會清除中斷標記(也就是說isInterrupted()返回值為false)
*/
Thread.sleep(6000);
} catch (InterruptedException e) {
//打印異常信息
// e.printStackTrace();
break;
}
}
System.out.println("線程已經(jīng)運行完畢...");
}
public? voidsetFlag(boolean flag){
this.runFlag = flag;
}
}
】
1.stop()方法届搁,不建議使用,要讓線程結(jié)束缘薛,就讓它執(zhí)行完;可以使用一個條件變量作為線程中循環(huán)條件窍育,然后在外界通過控制這個變量,來使得線程結(jié)束(終止線程宴胧,已被廢棄漱抓,不推薦使用)
2、設(shè)置標記
3.interrupt():中斷線程的阻塞狀態(tài)恕齐,調(diào)用該方法之后乞娄,線程會標記一個中斷標記,當線程處于阻塞(比如休眠) 狀態(tài)時显歧,如果線程具備中斷狀態(tài)仪或,就會直接中斷當前的阻塞狀態(tài),并去除中斷標記
(isInterrupted():測試線程是否已經(jīng)中斷)
PS:停止線程最本質(zhì)的原理其實就是讓run()方法執(zhí)行完
生命周期:生命周期:從創(chuàng)建到結(jié)束的整個過程追迟,在這期間會包含很多階段(狀態(tài))
新建狀態(tài)(New):MyThread??t1 = new MyThread();
就緒狀態(tài)(Runnable):調(diào)用start()方法,使線程具有了運行資格溶其,但是沒有CPU的執(zhí)行權(quán)(就緒隊列:CPU在分配執(zhí)行權(quán)的時候是在就緒隊列中隨機和挑選一個線程并分配其執(zhí)行權(quán)(CPU調(diào)度))骚腥,等待CPU調(diào)度
運行狀態(tài)(Running):獲取了CPU的執(zhí)行權(quán)敦间,只有在運行狀態(tài)下的線程才具備CPU的執(zhí)行權(quán)
阻塞狀態(tài)(Blocked):在運行狀態(tài)下調(diào)用了sleep、wait或其他的一切方法使線程進入阻塞狀態(tài)(睡眠池束铭、等待池)
【sleep(毫秒值)《睡眠池:一塊內(nèi)存池廓块,集合對象或數(shù)組》?? wait()《等待池》】
《睡眠結(jié)束(時間結(jié)束或被中斷)被喚醒(notify) -à>>>會再次進入就緒狀態(tài)》
死亡狀態(tài)(Dead):stop()??? run()執(zhí)行完《線程一旦結(jié)束進入死亡之后,就沒有用了契沫,該狀態(tài)是不可逆的》