dya19第十章线程
線程
1.程序 、進程和線程
程序: 靜態代碼,為了完成某種功能,使用某種計算機語言編寫的指令集合;
進程:運行中的程序,被加載到內存中,分配資源
線程:進程可以進一步被細化為線程,就是一個具體的執行任務,是操作系統調度中最小的單位
線程和進程的關系:
每一個進程都有相應的線程;在執行程序時,實際上是執行相應的一系列線程,進程是資源分配的最小單位,線程是程序執行的最小單位;[一個進程可以包含多個線程,一個線程只能屬于一個進程,線程不能脫離進程而獨立,也就是說當進程被關閉時,線程也會被自動關閉,每一個進程中至少包含一個線程,也就是主線程,在主線程中開始執行程序,java的程序的入口main()方法就是在主線程中被執行的,在主線程中可以創建并啟動其他線程, 一個進程內的所有線程共享該進程的內存資源。]
創建一個線程:
方式一:繼承Thread類;
方式二:繼承Runnable接口;
**繼承方式和實現方式的區別與聯系
區別: 繼承Thread:線程代碼存放在Thread子類run方法中。
實現Runnable:線程代碼在接口的子類run方法中;
**
實現Runnable的好處
1.避免了單繼承的局限性,提高了擴展性,
2.多個線程可以共享一個接口實現類的對象,非常適合多個相同的線程來處理同一份資源;
Thread中的方法:
Thread() 創建一個新的線程Thread(String name)創建一個指定名字的線程Thread類中的一些方法
**void Start() 啟動線程****final void setName(String name) 設置線程名稱****final Striing getName() 獲得線程名稱****final void setPriority(int newpriority) 設置線程優先級**線程優先級大的執行的優先權高,(并不是一直執行到完)**final int getPriority() 獲得線程優先級**final void jion() 等待程序終止**也就是說在使用這個方法之后的程序需要等待這個進程完成(死亡)后才能執行**static Thread currentThtead() 返回對當前執行對象的引用**Thread currentThread().getName() 可以獲得當前正在執行的這個程序的名字;**static void sleep(long millis) 讓線程休眠指定時間****Thread yield() 線程讓步(線程從運行態直接轉為就緒態)**stop() 停止程序**本質上不安全,已棄用 package day17.demo4; public class MyThread implements Runnable{@Overridepublic void run() {for (int i = 0; i < 100; i++) {System.out.println(Thread.currentThread().getName()+i);}} } package day17.demo4; public class Test1 {public static void main(String[] args) {MyThread myThread=new MyThread();//新建一個執行任務Thread thread1=new Thread(myThread,"thread1 ");//后面的參數為線程名稱Thread thread2=new Thread(myThread,"thread2 ");//獲得當前線程的線程名稱;currentThread():獲得當前線程;getName():獲取線程名稱;thread1.start();thread2.start();/** 時間片:給線程分配時間單位去執行;* 搶占式:高優先級的線程有更多的執行權,但也不是說低優先級的就沒有機會;具體得看cup對進程的調度;* *///thread1.getPriority()獲取thread1的線程優先級,一般為1-10;默認優先級為5thread1.setPriority(10);//設置線程優先級(高優先級的不一定先制性其優先權高,但不是一直執行完)thread2.setPriority(1);System.out.println("thread1的線程優先級為:"+thread1.getPriority());System.out.println("thread2的線程優先級為:"+thread2.getPriority());} }線程的優先級:
事實上,計算機只有一個cpu,各個線程輪流獲得cpu的使用權,執行任務;優先級高的線程有更多的被cpu執行的機會;反之則亦然;優先級是用整數表示的,取值范圍為1--10,在通常情況下,優先級默認為5;但可以通過final void setPriority(int newpriority) final int getPriority() 兩個方法來設置以及獲得優先級;調度策略:時間片:給線程分配時間去執行;搶占式:高優先級的線程有更高的 執行權,(但不是說低優先級的程序就沒有機會) Thread類有三個靜態常量來表示優先級 MAX_PRIORITTY:取值為10,表示最高的優先級;MIN_PRIORITY:取值為1,表示最低的優先級;NORM_PRIORITY;取值為5,表示默認的優先級;線程的狀態
線程在他的生命周期中會處于不同的狀態;
線程的生命周期: 從生到死;
線程的各狀態概念:
新建:當一個Thread類或其子類的對象被聲明并創建時,新生的線程對象處于新建狀態
就緒:處于新建狀態的線程被statr()后,將進入到線程隊列等到cpu的時間片,此時其已經具備了運行的條件,只是沒有被分配到cpu資源;
運行:當就緒的線程被調度并且得到了cpu的資源,便進入運行的狀態,run()方法定義了線程的操作和功能;
阻塞:在某種特殊情況下,被人為掛起或執行輸入輸出操作時,讓出cpu并臨時終止自己的執行,進入阻塞狀態;
死亡:線程完成了它的全部工作或者被線程提前強制性的終止或出現異常導致結束;
線程的分類
Java中的線程主要分為兩類,一類為用戶線程,一類為守護線程;
用個比較通俗的比較就是說是守護線程就是用戶線程的保姆,一般情況下只有當用戶線程全部結束時,時守護線程才能停止;
守護線程的作用:是為其他線程運行提供便利服務,守護線程最典型的應用就是GC(垃圾回收器),它就是一個很稱職的守護者;
用戶線程與守護者線程兩者之間幾乎沒有區別,唯一不同的之處就是虛擬機的離開,如果用戶線程已經全部退出運行了,只剩下守護線程存在了,虛擬機也就退出了,因為沒有了被守護者,守護者線程也就沒有工作可以做了,也就沒有繼續運行程序的必要了,
多線程的概念
多線程是指程序中包含多個執行單元,即在一個程序中可以同時運行多個不同的線程來執行不同的任務,也就是說允許單個程序創建多個并行執行的線程來完成各自的任務。
何時需要多線程
多線程的優點
提高程序的響應;提高cpu的利用率;改善程序結構,將復雜任務分為多個線程,獨立運行;多線程的缺點
線程也是程序,所以線程需要占用內存,線程越多占用的內存也越多(升級硬件設備來解決)多線程需要協調和管理,所以需要cpu時刻跟蹤線程;線程之間對共享資源的訪問會相互影響,必須解決競用共享資源的問題;線程的同步
并發與并行
并行:多個cpu同時執行多個任務,比如,多個人同時做不同的事;并發:一個cpu(采用時間片)同時執行多個任務,比如:秒殺,搶票,多個人做同一件事;多線程同步
多個線程同時讀寫同一份共享資源時,可能會引起沖突,所以引入線程"同步"機制,即各線程間要有先來后到;同步就是排隊加鎖:
幾個線程之間要排隊,一個個對共享資源進行操作,而不是同時進行操作; 為了保證數據在方法中被訪問時的正確性,在訪問時加入鎖機制;模擬買票
兩個窗口分別售票,票數為十張;分別使用繼承Thread類和實現Runnable接口兩種方式實現確保一個時間點只有一個線程訪問共享資源,可以給共享資源加一把鎖,哪個線程獲取了這把鎖,才有權力訪問該共享資源。
同步監視器
同步監視器可以是任何對象,必須唯一,保證多個線程獲得的是同一個對象(鎖)同步監視器的執行過程
1.第一個線程訪問,鎖定同步監視器,執行其中的代碼。2.第二個線程訪問,發現同步監視器被鎖定,無法訪問。3.第一個線程訪問完畢,解鎖同步監視器。4.第二個線程訪問,發現同步監視器沒有鎖,然后鎖定并訪問。一個線程持有鎖會導致其他所有需要此鎖的線程掛起;在多線程競爭下,加鎖與釋放鎖會導致比較多的上下文切換和調度延時,引起性能問題。
synchronized
案例:
模擬兩個窗口同時出售100張票
代碼如下:(繼承Thread類 給代碼塊加同步鎖)
同樣的案例:
模擬兩個窗口同時出售100張票
代碼如下:(繼承Thread類 給方法加同步鎖)
同樣下面用Runnable接口來實現:
代碼如下:(實現Runnable接口, 代碼塊加同步鎖)
代碼如下:(實現Runnable接口, 給方法加同步鎖)
Lock(鎖)
Lock
從JDK5.0開始,java提供了更強大的線程同步機制--通過顯示定義同步鎖對象來實現同步。同步鎖使用Lock對象充當。Java.util.concurrent.locks.Lock接口是控制多個線程對共享資源進行訪問的工具。鎖提供了對共享資源的獨占訪問,每次只能有一個線程對Lock對象加鎖,線程開始訪問共享資源之前應先獲得Lock對象。ReentrantLock類實現了Lock,它擁有與synchronized相同的并發性和內存語義,在實現線程安全的控制中,比較常用的是ReentrantLock,可以顯示加鎖,釋放鎖。同樣的案例:模擬兩個窗口出票
代碼如下:
Lock方法只能給代碼塊加同步鎖,不能給方法加,這是與synchronized的區別之一,還有一個區別就是syschronized是隱式的,Lock是顯示的。
線程死鎖
死鎖:
不用的線程分別占用對方需要的同步資源不放棄,都在等對方放棄自己需要的同步資源,于是就形成了線程的死鎖。出現死鎖后,不會出現異常,不會出現提示,所有線程都將處于阻塞狀態,無法繼續下去。例如;一個M國人和一張中國人一起吃飯正常情況為: M國人一刀一叉 中國人:兩根筷子特殊情況為:M國人一刀一根筷子 中國人;一叉一根筷子當遇到特殊情況時,兩人都不放棄對方需要的同步資源,就會一直等待下去,也就是線程死鎖。**設計程序時要考慮清楚鎖的順序,盡量減少嵌套的加鎖交互數量。**制造一個死鎖:
代碼如下:
產生死鎖一直等待;
案例:兩個線程交替打印1-100之間的數字
代碼如下:
線程通信
線程通訊指的是多個線程通過消息傳遞實現相互牽制,相互調度,即進程間的相互作用。
設計到三個方法:
經典案例:生產者消費者問題
生產者將產品放在柜臺,而消費者從柜臺處取走產品,生產者一次只能生產固定的數量,產品(例如一個),這時候柜臺中不能再放產品,此時生產者應該停止生產等待消費者拿走產品,此時生產者喚醒消費者來取走產品,消費者拿走產品后,喚醒生產者,消費者開始等待。代碼如下;
package Day18.demo6; /*柜臺,只有一個 */ public class Countdemo {int num=0;//生產//同步對象是this就是同一個Countdemo對象public synchronized void add(){if (num==0){num++;System.out.println("生產者線程生產了一個產品放在了柜臺上");this.notify();//此時喚醒消費者}else {try {this.wait();//此時已有產品如果此時生產者線程進來了就wait()生產者線程} catch (InterruptedException e) {e.printStackTrace();}}}//消費public synchronized void sub(){if (num==1){num--;System.out.println("消費者線程從柜臺上消費了一個產品");this.notify();//此時喚醒生產者}else {try {this.wait();//此時沒有產品,如果此時消費者進來就外套()消費者線程} catch (InterruptedException e) {e.printStackTrace();}}} } package Day18.demo6; //消費者線程 public class XFZdemo extends Thread{Countdemo countdemo;public XFZdemo(Countdemo countdemo) {this.countdemo=countdemo;}@Overridepublic void run() {while (true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}countdemo.sub();}} } package Day18.demo6; //生產者線程 public class SCZdemo extends Thread{Countdemo countdemo;public SCZdemo(Countdemo countdemo) {this.countdemo = countdemo;}@Overridepublic void run() {while (true){try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}countdemo.add();}} } package Day18.demo6; public class Test {public static void main(String[] args) {Countdemo c=new Countdemo();XFZdemo xfz=new XFZdemo(c);SCZdemo scz=new SCZdemo(c);xfz.start();scz.start();} }新增創建線程的方式
實現Callable接口與使用Runnable相比,Callable功能更加強大一些
相比run()方法,可以有返回值。方法可以拋出異常。支持泛型的返回值。據要借助FutureTask類,獲取返回結果。接受任務
FutureTask<Integer>futureTask=new FutureTask(任務);創建線程Thread t=new Thread(futureTask);t.start();Integer res=futureTask.get() 獲得線程call方法的返回值案例:
package Day18.demo7; import java.util.concurrent.Callable; public class CallableDemo implements Callable <Integer>{@Overridepublic Integer call() throws Exception {int num=0;for (int i = 0; i < 11; i++) {num+=i;}return num;} } package Day18.demo7; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class Test {public static void main(String[] args) throws ExecutionException, InterruptedException {Callable c=new CallableDemo();FutureTask<Integer> futureTask=new FutureTask<>(c);Thread t=new Thread(futureTask);t.start();Integer res=futureTask.get();System.out.println(res);} }總結
以上是生活随笔為你收集整理的dya19第十章线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTTP403使用Ajax的POST方法
- 下一篇: UOJ449. 【集训队作业2018】喂