2019-06-03 Java学习日记 day24 多线程
多線程
線程是程序執行的一臺路徑,一個進程中可以包含多條線程
多線程并發執行可以提高程序的效率,可以同時完成多項工作
多線程的應用背景
紅蜘蛛同時共享屏幕給多個電腦
迅雷開啟多條線程一起下載
QQ同時和多個人一起視頻
服務器同時處理多個客戶端請求
?
多線程并行和并發
并行就是兩個任務同時運行,就是甲任務進行的同時,乙任務也在進行(需要多核CPU)
并發是指兩個任務都請求運行,而處理器只能接受一個任務,就把這兩個任務輪流進行,由于時間間隔較短,使人感覺兩個任務都在運行
比如我跟兩個網友聊天,左手操作一個電腦跟甲聊,同時右手用另一臺電腦跟乙聊天,這就叫并行
如果用一臺電腦我先給甲發個信息,然后立刻再給乙發信息,然后再跟甲聊,再跟乙聊。這就叫并發
?
java程序運行原理和jvm的啟動
*java命令啟動java虛擬機,啟動jvm,等于啟動了一個應用程序,也就是啟動了一個進程,該進程會自動啟動一個 “ 主線程 ”,然后主線程去調用某個類的 main 方法
?
*jvm啟動至少啟動了垃圾回收線程和主線程,所以是多線程的
?
多線程程序實現
1. 繼承Thread
定義類繼承Thread
重寫run方法
把新線程要做的是寫在run方法中
創建線程對象
開啟新線程,內部會自動執行run方法
public class demo1_thread {public static void main(String[] args) {mythread mt1 =new mythread(); //創建thread類的子類對象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. 實現runnable
定義類顯示Runnable接口
實現run方法
把新線程要做的事寫在run方法中
創建自定義的Runnable的子類對象
創建Thread對象,傳入Runnable
調用start()開啟新線程,內部會自動調用Runnable的run方法
public class demo2_Runnnbale {public static void main (String args []){myrunnbale mr =new myrunnbale(); //創建runnable的子類對象new Thread(mr).start(); //將其當做參數傳遞給thread的構造函數,并開啟線程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");}}} 案例實現Runnable的原理
看Thread類的構造函數,傳遞了Runnable接口的引用
通過init()方法找到傳遞的target給成員變量的target賦值
查看run方法,發現run方法中有判斷,如果target不為null就會太哦用Runnable接口子類對象的run方法
?
兩種方式的區別
1.繼承Thread:由于子類重寫了Thread類的run(),方太哦用start()時,直接找子類的run()方法
2.實現Runnable:構造函數中傳入了Runnable的引用,成員變量記住了它,start()調用run()方法時內部成員變量Runnable引用是否為空,不為空編譯時看的siRunnable的run(),運行時執行的獅子類的run()方法
?
繼承Thread:
好處是:可以直接使用Thread類中的方法,代碼簡單
弊端是:如果已經有父類,就不能用這種方法
實現Runnable接口:
好處是:即使自己定義的縣城類有了父類也沒關系,因為有了父類也可以實現接口,而且接口是可以多實現的
弊端是:不能直接使用Thread中的方法需要先獲取到線程對象后,才能得到Thread的方法,代碼復雜
?
匿名內部類實現線程
1.繼承Thread方法
2.實現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()方法獲取線程對象的名字
設置名字
2.通過構造方法函數可以傳入String類型的名字
?
通過setName(String)方法可以設置線程對象的名字
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("戰三");System.out.println(this.getName()+"bb");}}.start();Thread t1= new Thread("大哥"){public void run(){System.out.println(this.getName()+"aaaaaaaa");}};t1.setName("李");t1.start();} } 案例?
獲取當前線程的對象
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()獲取當前正在執行的線程System.out.println(Thread.currentThread().getName()+"...bb");}}).start();Thread.currentThread().setName("主線程");System.out.println(Thread.currentThread().getName());}} 案例?
休眠線程
Thread.sleep(毫秒,納秒),控制當前線程休眠若干毫秒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("倒計時"+i+"秒");}}} 案例?
守護線程
setDaemon(),設置一個線程為守護線程,該線程不會單獨執行,當其他飛守護線程都執行結束后,自動退出
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); //當傳入true就是意味著設置為守護線程 t1.start();t2.start();}} 案例?
加入線程
join(),當前線程暫停,等待指定的線程執行結束后,當前線程在繼續
join(int),可以等待指定的毫秒之后繼續
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
?
設置線程的優先級
setPriority() 設置線程的優先級
N1.setPriority(Thread.MIN_PRIORITY);? ? ? ? ? ? //設置最大的線程優先級
N2.setPriority(Thread.MAX_PRIORITY);? ? ? ? ? ?//設置最小線程優先級
?
同步代碼塊
什么情況下需要同步
當多線程并發,有多段代碼同時執行是,我們希望某一段代碼執行的過程中cpu不要切換到其他線程工作,這是就需要同步
如果兩端代碼是同步的,那么同一時間值執行一段,在一段代碼每執行結束之前,不會執行另一端代碼
同步代碼塊
使用synchronized關鍵字加上一個鎖對象來定義一段代碼,這就叫同步代碼塊
多個同步代碼塊如果使用相同的鎖對象,那么他們就是同步的
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("學習");}}public void print2(){System.out.println("學校");} }class demo{} 案例?
同步方法
*使用synchronized 關鍵字修飾一個方法,該方法中所有的代碼都是同步的
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{//非靜態放入同步方法的鎖對象是this//靜態的鎖不方法的鎖對象是:該類的字節碼對象public synchronized void print1(){System.out.print("學");System.out.print("習");System.out.print("的");System.out.print("心");System.out.print("\r\n");}public void print2(){synchronized (this) { System.out.print("學");System.out.print("校");System.out.print("的");System.out.print("路");System.out.print("上");System.out.print("\r\n");}}} 案例?
線程安全問題
多線程并發操作同一數據時,就有可能出現線程安全問題
使用同步技術可以解決這種問題,把操作數據的代碼進行同步,不要多個線程一起操作
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-- + "號票");}}} } 案例?
死鎖
多線程同步的時候,如果同步代碼嵌套,使用相同鎖,就有可能出現死鎖
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();}} 案例?
轉載于:https://www.cnblogs.com/JungTan0113/p/10970951.html
總結
以上是生活随笔為你收集整理的2019-06-03 Java学习日记 day24 多线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【转载】Sqlserver限制最大可使用
- 下一篇: LeetCode - 695. Max