2019-06-03 Java学习日记 day24 多线程
多線程
線程是程序執(zhí)行的一臺(tái)路徑,一個(gè)進(jìn)程中可以包含多條線程
多線程并發(fā)執(zhí)行可以提高程序的效率,可以同時(shí)完成多項(xiàng)工作
多線程的應(yīng)用背景
紅蜘蛛同時(shí)共享屏幕給多個(gè)電腦
迅雷開啟多條線程一起下載
QQ同時(shí)和多個(gè)人一起視頻
服務(wù)器同時(shí)處理多個(gè)客戶端請(qǐng)求
?
多線程并行和并發(fā)
并行就是兩個(gè)任務(wù)同時(shí)運(yùn)行,就是甲任務(wù)進(jìn)行的同時(shí),乙任務(wù)也在進(jìn)行(需要多核CPU)
并發(fā)是指兩個(gè)任務(wù)都請(qǐng)求運(yùn)行,而處理器只能接受一個(gè)任務(wù),就把這兩個(gè)任務(wù)輪流進(jìn)行,由于時(shí)間間隔較短,使人感覺兩個(gè)任務(wù)都在運(yùn)行
比如我跟兩個(gè)網(wǎng)友聊天,左手操作一個(gè)電腦跟甲聊,同時(shí)右手用另一臺(tái)電腦跟乙聊天,這就叫并行
如果用一臺(tái)電腦我先給甲發(fā)個(gè)信息,然后立刻再給乙發(fā)信息,然后再跟甲聊,再跟乙聊。這就叫并發(fā)
?
java程序運(yùn)行原理和jvm的啟動(dòng)
*java命令啟動(dòng)java虛擬機(jī),啟動(dòng)jvm,等于啟動(dòng)了一個(gè)應(yīng)用程序,也就是啟動(dòng)了一個(gè)進(jìn)程,該進(jìn)程會(huì)自動(dòng)啟動(dòng)一個(gè) “ 主線程 ”,然后主線程去調(diào)用某個(gè)類的 main 方法
?
*jvm啟動(dòng)至少啟動(dòng)了垃圾回收線程和主線程,所以是多線程的
?
多線程程序?qū)崿F(xiàn)
1. 繼承Thread
定義類繼承Thread
重寫run方法
把新線程要做的是寫在run方法中
創(chuàng)建線程對(duì)象
開啟新線程,內(nèi)部會(huì)自動(dòng)執(zhí)行run方法
public class demo1_thread {public static void main(String[] args) {mythread mt1 =new mythread(); //創(chuàng)建thread類的子類對(duì)象mt1.start(); //開啟線程for(int i=0;i<1000;i++){System.out.println("bbbbb");}}} class mythread extends Thread{ //繼承threadpublic void run(){ //重寫run方法for(int i=0;i<1000;i++){System.out.println("aaaaaaaaaa");}} } 案例?
2. 實(shí)現(xiàn)runnable
定義類顯示Runnable接口
實(shí)現(xiàn)run方法
把新線程要做的事寫在run方法中
創(chuàng)建自定義的Runnable的子類對(duì)象
創(chuàng)建Thread對(duì)象,傳入Runnable
調(diào)用start()開啟新線程,內(nèi)部會(huì)自動(dòng)調(diào)用Runnable的run方法
public class demo2_Runnnbale {public static void main (String args []){myrunnbale mr =new myrunnbale(); //創(chuàng)建runnable的子類對(duì)象new Thread(mr).start(); //將其當(dāng)做參數(shù)傳遞給thread的構(gòu)造函數(shù),并開啟線程for (int i = 0; i < 1000; i++) {System.out.println("cc");}} } class myrunnbale implements Runnable{@Overridepublic void run() {for (int i = 0; i < 1000; i++) {System.out.println("aaaaaaaaaaaaaa");}}} 案例實(shí)現(xiàn)Runnable的原理
看Thread類的構(gòu)造函數(shù),傳遞了Runnable接口的引用
通過init()方法找到傳遞的target給成員變量的target賦值
查看run方法,發(fā)現(xiàn)run方法中有判斷,如果target不為null就會(huì)太哦用Runnable接口子類對(duì)象的run方法
?
兩種方式的區(qū)別
1.繼承Thread:由于子類重寫了Thread類的run(),方太哦用start()時(shí),直接找子類的run()方法
2.實(shí)現(xiàn)Runnable:構(gòu)造函數(shù)中傳入了Runnable的引用,成員變量記住了它,start()調(diào)用run()方法時(shí)內(nèi)部成員變量Runnable引用是否為空,不為空編譯時(shí)看的siRunnable的run(),運(yùn)行時(shí)執(zhí)行的獅子類的run()方法
?
繼承Thread:
好處是:可以直接使用Thread類中的方法,代碼簡(jiǎn)單
弊端是:如果已經(jīng)有父類,就不能用這種方法
實(shí)現(xiàn)Runnable接口:
好處是:即使自己定義的縣城類有了父類也沒關(guān)系,因?yàn)橛辛烁割愐部梢詫?shí)現(xiàn)接口,而且接口是可以多實(shí)現(xiàn)的
弊端是:不能直接使用Thread中的方法需要先獲取到線程對(duì)象后,才能得到Thread的方法,代碼復(fù)雜
?
匿名內(nèi)部類實(shí)現(xiàn)線程
1.繼承Thread方法
2.實(shí)現(xiàn)Runnable方法
public class demo3_Thread {public static void main(String[] args) {new Thread(){ //繼承Thread方法public void run(){ //重寫run方法for (int i = 0; i < 3000; i++) {System.out.println("aaaaaaaaa");}}}.start(); //開啟線程new Thread(new Runnable(){public void run(){for (int i = 0; i < 3000; i++) {System.out.println("bbb");}}}).start();}} 案例?
獲取名字
1.通過geyName()方法獲取線程對(duì)象的名字
設(shè)置名字
2.通過構(gòu)造方法函數(shù)可以傳入String類型的名字
?
通過setName(String)方法可以設(shè)置線程對(duì)象的名字
public class demo1_Name {public static void main (String args[]){new Thread("大哥"){public void run(){System.out.println(this.getName()+"aaaaaaaa");}}.start();new Thread(){public void run(){this.setName("戰(zhàn)三");System.out.println(this.getName()+"bb");}}.start();Thread t1= new Thread("大哥"){public void run(){System.out.println(this.getName()+"aaaaaaaa");}};t1.setName("李");t1.start();} } 案例?
獲取當(dāng)前線程的對(duì)象
Thread.currentThread(),主線程也可以獲取
public class demo2_currentthread {public static void main(String[] args) {new Thread("毛毛"){public void run (){System.out.println(getName()+"...aaaaaaa");}}.start();new Thread(new Runnable(){public void run(){//Thread.currentThread()獲取當(dāng)前正在執(zhí)行的線程System.out.println(Thread.currentThread().getName()+"...bb");}}).start();Thread.currentThread().setName("主線程");System.out.println(Thread.currentThread().getName());}} 案例?
休眠線程
Thread.sleep(毫秒,納秒),控制當(dāng)前線程休眠若干毫秒1秒 =1000 * 1000 * 1000納秒? 1000000000
public class demo3_seelp {public static void main(String[] args) throws InterruptedException {//demo1();new Thread(){public void run(){for (int i = 20; i >=0; i--) {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}System.out.println(getName()+"...a");}}}.start();new Thread(){public void run(){for (int i = 20; i >=0; i--) {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}System.out.println(getName()+"..bb");}}}.start();}public static void demo1() throws InterruptedException {for(int i=20;i>=0;i--){Thread.sleep(1000);System.out.println("倒計(jì)時(shí)"+i+"秒");}}} 案例?
守護(hù)線程
setDaemon(),設(shè)置一個(gè)線程為守護(hù)線程,該線程不會(huì)單獨(dú)執(zhí)行,當(dāng)其他飛守護(hù)線程都執(zhí)行結(jié)束后,自動(dòng)退出
public class demo4_daemon {public static void main(String[] args) {Thread t1 =new Thread(){public void run(){for (int i = 0; i < 2; i++) {System.out.println(getName()+"..aaaaaaaa");}}};Thread t2 =new Thread(){public void run(){for (int i = 0; i < 50; i++) {System.out.println(getName()+"..bb");}}};t2.setDaemon(true); //當(dāng)傳入true就是意味著設(shè)置為守護(hù)線程 t1.start();t2.start();}} 案例?
加入線程
join(),當(dāng)前線程暫停,等待指定的線程執(zhí)行結(jié)束后,當(dāng)前線程在繼續(xù)
join(int),可以等待指定的毫秒之后繼續(xù)
public class demo5_Join {public static void main(String[] args) {final Thread t1 =new Thread(){public void run (){for(int i=0; i<10 ;i++){System.out.println(getName()+"...aaaaaaaaa");}}};Thread t2 =new Thread(){public void run (){for(int i=0; i<10 ;i++){if(i==2){try {t1.join(1);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(getName()+"...bb");}}};t1.start();t2.start();}} 案例?
禮讓線程
yield讓出cpu
?
設(shè)置線程的優(yōu)先級(jí)
setPriority() 設(shè)置線程的優(yōu)先級(jí)
N1.setPriority(Thread.MIN_PRIORITY);? ? ? ? ? ? //設(shè)置最大的線程優(yōu)先級(jí)
N2.setPriority(Thread.MAX_PRIORITY);? ? ? ? ? ?//設(shè)置最小線程優(yōu)先級(jí)
?
同步代碼塊
什么情況下需要同步
當(dāng)多線程并發(fā),有多段代碼同時(shí)執(zhí)行是,我們希望某一段代碼執(zhí)行的過程中cpu不要切換到其他線程工作,這是就需要同步
如果兩端代碼是同步的,那么同一時(shí)間值執(zhí)行一段,在一段代碼每執(zhí)行結(jié)束之前,不會(huì)執(zhí)行另一端代碼
同步代碼塊
使用synchronized關(guān)鍵字加上一個(gè)鎖對(duì)象來(lái)定義一段代碼,這就叫同步代碼塊
多個(gè)同步代碼塊如果使用相同的鎖對(duì)象,那么他們就是同步的
public class test_sybchronized {public static void main(String[] args) {final pritner p1=new pritner();new Thread(){public void run(){while(true){p1.print1(); }}}.start();new Thread(){public void run(){while(true){p1.print2(); }}}.start();}} class pritner{ demo d=new demo();public void print1(){synchronized (d) {System.out.println("學(xué)習(xí)");}}public void print2(){System.out.println("學(xué)校");} }class demo{} 案例?
同步方法
*使用synchronized 關(guān)鍵字修飾一個(gè)方法,該方法中所有的代碼都是同步的
public class test2_sybchronized {public static void main(String[] args) {final pritner2 p1=new pritner2();new Thread(){public void run(){while(true){p1.print1(); }}}.start();new Thread(){public void run(){while(true){p1.print2(); }}}.start();}} class pritner2{//非靜態(tài)放入同步方法的鎖對(duì)象是this//靜態(tài)的鎖不方法的鎖對(duì)象是:該類的字節(jié)碼對(duì)象public synchronized void print1(){System.out.print("學(xué)");System.out.print("習(xí)");System.out.print("的");System.out.print("心");System.out.print("\r\n");}public void print2(){synchronized (this) { System.out.print("學(xué)");System.out.print("校");System.out.print("的");System.out.print("路");System.out.print("上");System.out.print("\r\n");}}} 案例?
線程安全問題
多線程并發(fā)操作同一數(shù)據(jù)時(shí),就有可能出現(xiàn)線程安全問題
使用同步技術(shù)可以解決這種問題,把操作數(shù)據(jù)的代碼進(jìn)行同步,不要多個(gè)線程一起操作
public class test2_ticket {public static void main(String[] args) {new ticket().start();new ticket().start();new ticket().start();new ticket().start();}} class ticket extends Thread{private static int ticket =100;public void run(){while(true){synchronized (ticket.class) { if(ticket <=0){break;}try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}System.out.println(getName() + "...這是第" + ticket-- + "號(hào)票");}}} } 案例?
死鎖
多線程同步的時(shí)候,如果同步代碼嵌套,使用相同鎖,就有可能出現(xiàn)死鎖
public class test3_ {private static String s1 ="筷子左";private static String s2 ="筷子右";public static void main(String[] args) {new Thread(){public void run (){while(true){synchronized (s1) {System.out.println(getName()+"讀取"+s1+"等待"+s2); synchronized (s2) {System.out.println(getName()+"拿到"+s2+"吃"); }}}}}.start();new Thread(){public void run (){while(true){synchronized (s2) {System.out.println(getName()+"讀取"+s2+"等待"+s1); synchronized (s1) {System.out.println(getName()+"拿到"+s1+"吃"); }}}}}.start();}} 案例?
轉(zhuǎn)載于:https://www.cnblogs.com/JungTan0113/p/10970951.html
總結(jié)
以上是生活随笔為你收集整理的2019-06-03 Java学习日记 day24 多线程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转载】Sqlserver限制最大可使用
- 下一篇: UVA11624 Fire!