進程:每個進程(程序)都有獨立的代碼和數據空間(進程上下文)。進程間的切換會有較大的開銷,一個進程包括1--n個線程。(進程是資源分配的最小單位)java
線程:同一類線程共享代碼和數據空間,每個線程有獨立的執行棧和程序計數器(PC),線程切換開銷小。(線程是cpu調度的最小單位)網絡
多進程:是指操做系統能同一時候執行多個任務(程序)。多線程
多線程:是指在同一程序中有多個順序流在執行。併發
在java中要想實現多線程,主要有兩種方式,繼承Thread類或者實現Runable接口,至於實現Callable接口方式,不經常使用ide
不推薦繼承Thread類,由於java是單繼承,能實現接口使用的不推薦使用繼承,而且Thread類也是實現了Runable接口,而且提供了run()方法用於啓動線程工具
package com.bjsxt.thread; /** * Created by Administrator on 2019/3/9. */ public class TestThread01 extends Thread{ /** * 線程的入口點 */ @Override public void run() { for (int i=0;i<100;i++){ System.out.println("一遍聽歌。。。。"); } } public static void main(String[] args){ TestThread01 testThread01=new TestThread01(); //建立對象 //啓動線程,不保證會當即執行,只是建立了個線程,把執行權交給cpu,至於何時執行要看cpu調度 //若是寫成testThread01.run()就是普通方法的調用了 testThread01.start(); for(int i=0;i<100;i++){ System.out.println("一遍敲代碼。。。"); } } }
執行結果測試
案例:同時開啓三個線程下載一個網絡url圖片ui
一、先寫一個文件下載的工具類FileUtilsthis
package com.bjsxt.thread; import java.io.*; import java.net.MalformedURLException; import java.net.URL; /** * Created by Administrator on 2019/3/9. */ public class FileUtils { /** * * @param url 文件遠程url路徑 * @param name 文件保存位置 */ public void copyURLFile(String url,String name){ try( //建立文件輸入緩衝流,讀取文件信息 BufferedInputStream bis=new BufferedInputStream(new URL(url).openStream()); //建立文件輸出流,寫出文件信息 BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(name)); ) { byte[] bytes=new byte[1024]; int len=-1; while((len=bis.read(bytes))!=-1){ bos.write(bytes,0,len); bos.flush(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } } }
建立線程類,開啓下載url
package com.bjsxt.thread; /** * Created by Administrator on 2019/3/9. */ public class TestThread02 extends Thread { private String name; private String path; public TestThread02(String name, String path) { this.name = name; this.path = path; } @Override public void run() { System.out.println(name); FileUtils fileUtils=new FileUtils(); fileUtils.copyURLFile(path,name); } public static void main(String[] args){ TestThread02 testThread01=new TestThread02("11.jpg","https://jiahongdichan.oss-cn-shenzhen.aliyuncs.com/data/2019-01-05/uploads/banner/jpg/59011546679793431.jpg"); TestThread02 testThread02=new TestThread02("22.jpg","https://jiahongdichan.oss-cn-shenzhen.aliyuncs.com/data/2019-01-05/uploads/banner/jpg/16651546679105625.jpg"); TestThread02 testThread03=new TestThread02("33.jpg","https://jiahongdichan.oss-cn-shenzhen.aliyuncs.com/data/2019-01-05/uploads/banner/jpg/24221546678934817.jpg"); testThread01.start(); //開啓線程1 testThread02.start(); //開啓線程2 testThread03.start(); //開啓線程3 } }
package com.bjsxt.thread; /** * Created by Administrator on 2019/3/9. */ public class TestThread03 implements Runnable { /** * 線程入口 */ @Override public void run() { for (int i=0;i<100;i++){ System.out.println("一遍聽歌。。。。"); } } public static void main(String[] args){ //創線程實現類 TestThread03 testThread03=new TestThread03(); //建立代理對象 Thread thread=new Thread(testThread03); //開啓線程 thread.start(); for(int i=0;i<100;i++){ System.out.println("一遍敲代碼。。。"); } } }
案例1:搶票,三個線程共享100張票
從這個案例能夠看到實現Runable接口實現的多線程,能夠共享資源。而繼承Thread卻不行
package com.bjsxt.thread; /** * Created by Administrator on 2019/3/9. */ public class TestThread04 implements Runnable { private int ticket=100; //總票數,當前線程共享100個票 @Override public void run() { while(true){ if(ticket<0){ break; } try { Thread.sleep(100); //線程睡100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } //獲取當前線程的名字 System.out.println(Thread.currentThread().getName()+"->"+ticket--); } } public static void main(String[] args){ TestThread04 testThread04=new TestThread04(); new Thread(testThread04,"線程1").start(); //開啓線程,該線程的名字爲線程1 new Thread(testThread04,"線程2").start(); new Thread(testThread04,"線程3").start(); } }
執行結果,咱們看到輸出的有-1,-2,而咱們明明控制的當票數小於0,會退出循環,這就是出現了併發訪問問題。
案例2:龜兔賽跑問題
package com.bjsxt.thread; /** * 模擬龜兔賽跑 */ public class TestThread05 implements Runnable { private String successPerson; //記錄勝利者 @Override public void run() { for(int steps=0;steps<=100;steps++){ System.out.println(Thread.currentThread().getName()+"->"+steps); //打印當前步數 boolean flag=isSuccess(steps); if(flag){ break; } } } private boolean isSuccess(int steps){ if(successPerson!=null){ return true; }else{ if(steps==100){ successPerson=Thread.currentThread().getName(); System.out.println(successPerson); return true; } } return false; } public static void main(String[] args){ TestThread05 testThread05=new TestThread05(); new Thread(testThread05,"wugui").start(); new Thread(testThread05,"tuzi").start(); } }
package com.bjsxt.thread; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 模擬龜兔賽跑 * 第三種實現線程的方式:實現Callable接口,接口的泛型爲call()方法的返回值類型 */ public class TestThread07 implements Callable<Boolean> { private String successPerson; //記錄勝利者 @Override public Boolean call()throws Exception { for(int steps=0;steps<=100;steps++){ System.out.println(Thread.currentThread().getName()+"->"+steps); //打印當前步數 boolean flag=isSuccess(steps); if(flag){ break; } } return true; } private boolean isSuccess(int steps){ if(successPerson!=null){ return true; }else{ if(steps==100){ successPerson=Thread.currentThread().getName(); System.out.println(successPerson); return true; } } return false; } public static void main(String[] args)throws Exception{ TestThread07 testThread05=new TestThread07(); //建立執行服務 ExecutorService es= Executors.newFixedThreadPool(2); //提交執行 Future<Boolean> future1=es.submit(testThread05); Future<Boolean> future2=es.submit(testThread05); //獲取結果 boolean b1=future1.get(); boolean b2=future2.get(); //關閉服務 es.shutdownNow(); } }
線程有五大狀態,分別是:新生狀態,就緒狀態,運行狀態,阻塞狀態,死亡狀態。
(1)線程中止
方法1:方法體執行完線程自會中止
方法2:調用線程(Thread類)提供的stop()/destroy()方法,不推薦,存在問題,已經被廢棄
方法3:提供一個boolean型的終止變量,當這個變量置爲false,則終止線程的運行(能夠按期去檢查這個變量)
package com.bjsxt.thread.state; /** * 中止線程 * */ public class TestThreadStop01 implements Runnable{ private boolean flag=true; //當是true線程才執行 @Override public void run() { int i=0; while(flag){ System.out.println(Thread.currentThread().getName()+"->"+i++); } } /** * 改變標記爲false */ public void myStop(){ this.flag=false; } public static void main(String[] args){ TestThreadStop01 testThreadStop01=new TestThreadStop01(); new Thread(testThreadStop01,"線程1").start(); for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"->"+i); } testThreadStop01.myStop(); //改變標識,中止線程 } }
(2)sleep方法
特色:
Thread.sleep(1000)當前線程阻塞的毫秒數,sleep可讓線程進入阻塞狀態。
sleep時間達到後線程進入就緒狀態。
sleep阻塞線程時,不會釋放鎖。
sleep()方法是Thread類的靜態方法,會阻塞全部執行該行代碼的線程。
package com.bjsxt; /** * Created by Administrator on 2019/4/8. */ public class TestSleep { public static void main(String[] args){ Ticket ticket=new Ticket(); new Thread(ticket,"線程1").start(); new Thread(ticket,"線程2").start(); } } class Ticket implements Runnable{ private int ticketNum=100; //共有100張票 @Override public void run() { while(true){ if(ticketNum>0){ try { Thread.sleep(100); System.out.println("第"+--ticketNum+"張票"); } catch (InterruptedException e) { e.printStackTrace(); } }else{ break; } } } }
(3)yield方法
特色:
Thread.yield() 禮讓線程,讓當前正在執行線程暫停,不是阻塞線程,而是將線程從運行狀態轉入就緒狀態,讓cpu調度器從新進行調度
yield()方法是Thread類的靜態方法,會做用於全部執行該行代碼的線程。
package com.bjsxt; /** * Created by Administrator on 2019/4/8. */ public class TestYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"start......"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"end......"); } public static void main(String[] args){ TestYield testYield=new TestYield(); new Thread(testYield,"線程1").start(); new Thread(testYield,"線程2").start(); } }
(4)join方法
特色:join合併線程,待此線程執行完成後,再執行其餘線程,其餘線程阻塞
join(long mm)方法能夠傳一個參數毫秒數
join是成員方法,不是靜態方法,當線程對象調用該方法時,會阻塞執行這段代碼的線程,等到該線程對象執行完成或者時間到了,會讓cpu調度器從新進行調度
package com.bjsxt; /** * Created by Administrator on 2019/4/9. */ public class TestJoin { public static void main(String[] args){ Thread thread=new Thread(new Runnable() { @Override public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+i); } } },"線程a"); thread.start(); Thread thread2=new Thread(new Runnable() { @Override public void run() { for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+i); } } },"線程b"); thread2.start(); for(int i=0;i<100;i++){ if(i==20){ try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+i); } } }
(5)獲取線程的狀態
package com.bjsxt; /** * 測試線程狀態 */ public class TestThreadState { public static void main(String[] args){ Thread thread=new Thread(()->{ for(int i=0;i<10;i++){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread.State state=thread.getState(); //獲取該線程的當前狀態 System.out.println(state); thread.start(); System.out.println(thread.getState()); } }
Java提供一個線程調度器來監控程序中啓動後進入就緒狀態的全部線程。線程調度器按照線程的優先級決定應調度哪一個線程來執行。
線程的優先級用數字表示,範圍從1到10
• Thread.MIN_PRIORITY = 1
• Thread.MAX_PRIORITY = 10
• Thread.NORM_PRIORITY = 5
使用下述方法得到或設置線程對象的優先級。
• int getPriority();
• void setPriority(int newPriority);
優先級的設定建議在start()調用前
注意:優先級低只是意味着得到調度的機率低。並非絕對先調用優先級高後調用優先級低的線程。
package com.bjsxt; import org.omg.PortableInterceptor.SYSTEM_EXCEPTION; /** * 測試線程優先級 */ public class TestPriority { public static void main(String[] args){ MyPriority myPriority=new MyPriority(); Thread thread1=new Thread(myPriority,"線程a"); Thread thread2=new Thread(myPriority,"線程b"); Thread thread3=new Thread(myPriority,"線程c"); Thread thread4=new Thread(myPriority,"線程d"); Thread thread5=new Thread(myPriority,"線程e"); thread1.setPriority(10); thread2.setPriority(9); thread3.setPriority(8); thread4.setPriority(2); thread5.setPriority(1); thread1.start(); thread2.start(); thread3.start(); thread4.start(); thread5.start(); } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+Thread.currentThread().getPriority()); } }
• 線程分爲用戶線程和守護線程;
• 虛擬機必須確保用戶線程執行完畢;
• 虛擬機不用等待守護線程執行完畢;
• 如後臺記錄操做日誌、監控內存使用等
package com.bjsxt; /** * 手動建立的線程默認都是用戶線程 */ public class TestThreadType { public static void main(String[] args){ Thread thread=new Thread(new God()); thread.setDaemon(true); //把該線程設置爲守護線程 thread.start(); new Thread(new You()).start(); } } class You implements Runnable{ @Override public void run() { for(int i=0;i<100;i++){ System.out.println("用戶線程"+i); } } } class God implements Runnable{ @Override public void run() { while(true){ System.out.println("守護線程在運行。。。"); } } }
isAlive() 判斷線程是否還活着,即線程是否還未終止setName() 給線程起一個名字getName() 獲取線程的名字currentThread() 取得當前正在運行的線程對象,也就是獲取本身自己