總結:
1.繼承方式建立線程,步驟
1.1建立一個類繼承Thread
1.2該類實現run方法
1.3在須要開闢線程的地方,建立該類的實例,一個實例表示一個線程
1.4調用Thread類的start()方法,啓動線程,則它會執行run()方法java
2.實現方式建立線程,步驟
2.1建立一個類實現Runnable接口
2.2該類實現run方法
2.3在須要開闢線程的地方,建立該類的實例,將該類的實例做爲形參傳入Thread類的構造器中,建立Thread類的實例,該實例即爲線程。 2.4調用Thread類的start()方法,啓動線程,則它會執行run()方法安全
3.Thread類經常使用方法
3.1 start():啓動線程,執行當前線程的run方法
3.2 run():執行子線程的邏輯業務
3.3 currentThread():獲取當前線程
3.4 getName()與setName():設置/獲取線程名稱
3.5 yield():釋放當前線程的cpu執行權,可是它會參與下一個片刻的cpu爭搶
3.6 join():在A線程中,B線程執行join()方法,等着B線程執行結束,A線程再執行
3.7 isAlive():判斷當前線程是否在還活着
3.8 sleep():使線程沉睡多少秒多線程
4.線程的生命週期
4.1新建:該線程處於建立狀態
4.2就緒:處於建立狀態的線程執行start()方法後,等待CPU時間片
4.3運行:就緒狀態的線程搶到了CPU執行權,執行run()方法
4.4阻塞:運行中的線程被掛起,即讓出CPU執行權
4.5死亡:線程完成了它的工做,或者線程被強制性停止app
5.線程同步
其實是加鎖
1.同步代碼塊
添加監視器在 操做共享數據的代碼塊上
2.同步方法
添加synchronized在 操做共享數據的代碼塊的方法上ide
6.線程通訊
線程通訊有三個方法
wait():掛起當前線程,釋放CPU執行權
notify():喚醒排隊中優先級最高的那個線程
notifyAll():喚醒全部排隊的線程測試
測試:兩個線程交替打印輸出1-100,步驟
1.實現多線程功能,兩個線程可能會打印出同一個數據
2.實現線程同步,一個線程輸出一個數據,但沒有實現交替
3.實現交替功能,在同步方法中
3.1喚醒線程
3.2該線程執行 操做共享數據方法
3.3掛起當前線程,即讓另外一個線程進來
3.4重複一、二、3this
###1.基本概念
1.程序:爲了完成某一特定功能基於某種語言編寫的一組指令的集合,即一段靜態的代碼。
2.進程:程序的一次運行過程,即正在運行的程序。
3.線程:程序中的一個分支,一個進程能夠同時運行多個線程,簡稱多線程。
###2.建立線程方式
####方式1:繼承Thread類線程
/** * 方式1:繼承方式建立線程 * 步驟: * 1.建立一個類繼承Thread * 2.重寫Thread類的run()方法,該方法內實現子線程須要完成的功能 * 3.在須要開闢該線程的地方建立該線程的實例對象 * 4.調用Thread類的start()方法,啓動子線程,則會調用run()方法 */ public class SubThread extends Thread{ public void run() { for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
測試代碼:code
public static void main(String[] args) { SubThread st = new SubThread(); //給子線程設置名字 st.setName("子線程"); //執行start()方法時,纔是真正的開闢了子線程 st.start(); //給主線程設置名字 Thread.currentThread().setName("主線程"); //主線程實現功能 for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } }
輸出結果對象
子線程:50 子線程:51 子線程:52 主線程:48 子線程:53 主線程:49 子線程:54 主線程:50 ......
備註:由結果可知,子線程執行了。
####方式2:實現Runnable接口方式
package com.test.thread; /** * 方式2:實現方式建立進程 * 步驟: * 1.建立一個類,實現Runnable接口 * 2.重寫Thread類的run()方法,該方法內實現子線程須要完成的功能 * 3.在須要開闢該線程的地方建立該線程的實例對象 * 4.將此對象做爲形參傳遞給Thread類的構造器中,建立Thread類的實例對象,此對象即爲一個線程,要想建立多個線程,即須要實例化多個對象 * 5.調用Thread類的start()方法,啓動子線程,則會調用run()方法 */ public class SubThread2 implements Runnable{ @Override public void run() { for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } }
測試代碼
public static void main(String[] args) { SubThread2 st2 = new SubThread2(); //線程1 Thread thread1 = new Thread(st2); //線程2 Thread thread2 = new Thread(st2); thread1.start(); thread2.start(); }
###3.Thread經常使用方法
1.start():啓動線程,並執行該線程中run()方法
2.run():子線程藥執行的邏輯業務 3.currentThread():靜態方法,返回當前Thread
4.getName():獲取當前線程名稱
5.setName():給當前線程設置名稱
6.yield():調用此方法的線程釋放當前CPU的執行權
//主線程實現功能 for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); if (i>10) { //釋放當前CPU執行權,可是本身的功能還未執行完,下一個片刻接着爭搶cpu執行權 Thread.yield(); } }
7.join():在A線程中,B線程執行join()方法,則A線程暫停,直至B線程執行結束,A線程再執行。
//主線程實現功能 for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); if (i>10) { try { //當前線程暫停,子線程優先執行完;子線程執行完後,主線程接着執行完 st.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
8.isAlive:判斷該線程是否還存活
//主線程實現功能 for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); if (i>10) { try { //當前線程暫停,子線程優先執行完;子線程執行完後,主線程接着執行完 st.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //很明顯st線程已經死掉了 System.out.println(st.isAlive()); }
9.sleep(n):靜態方法,調用該方法的線程睡眠n毫秒。
public class SubThread extends Thread{ public void run() { for (int i = 0; i <= 100; i++) { try { //該線程睡眠1秒鐘 SubThread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":"+i); } } }
###4.線程的調度
####1.調度策略
1.1 時間片輪轉:即給進程中的每一個線程相同的時間,輪流使用cpu
1.2 優先級:即優先級高的先使用cpu
public static void main(String[] args) { SubThread st = new SubThread(); //給子線程設置名字 st.setName("子線程"); //設置子線程優先級 st.setPriority(Thread.MAX_PRIORITY); //執行start()方法時,纔是真正的開闢了子線程 st.start(); //給主線程設置名字 Thread.currentThread().setName("主線程"); //設置主線程優先級 Thread.currentThread().setPriority(Thread.NORM_PRIORITY); //主線程實現功能 for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } }
###5.實現方式多線程示例
在實際工做中,線程實現方式比繼承方式用的多。以下示例是多窗口售票
package com.test.thread; class Window implements Runnable{ //總共的票數 //基於該對象的多個線程共享該資源 int ticket = 100; @Override public void run() { while (true) { if (ticket > 0) { System.out.println(Thread.currentThread().getName()+"售票,票號爲:"+ticket--); }else { break; } } } } public class Main { public static void main(String[] args) { Window window = new Window(); //線程1 Thread thread1 = new Thread(window); //線程2 Thread thread2 = new Thread(window); //線程3 Thread thread3 = new Thread(window); //開啓線程 thread1.start(); thread2.start(); thread3.start(); } }
###6.線程生命週期(5種狀態)
####1.新建
Thread類或其子類的對象被聲明建立時,則新生的線程處於建立狀態。
####2.就緒
處於新建狀態的線程被start()後,將進入線程隊列等待CPU時間片,此時它處於就緒狀態。
####3.運行
當就緒的線程被調度並獲取處理器資源時,便進入運行狀態,run()方法裏面定義了該線程要實現的功能。
####4.阻塞
線程被人爲掛起,或執行輸入輸出操做時,讓出CPU並臨時停止本身的執行,進入阻塞狀態
####5.死亡
線程完成了它的所有工做,或線程提早被強制性停止。 ###7.線程的同步機制
####1.線程安全出現的緣由?
多個線程同時操做共享數據,其中一個線程在操做共享數據的過程當中,未執行完畢的狀況下,其它的線程也參與進來,致使共享數據存在了安全問題。
####2.如何解決線程安全問題?
線程的同步機制
#####方法1:同步代碼塊
synchronized(同步監視器) { //須要被同步的代碼塊,即操做共享數據的代碼 } 1.共享數據:多個線程能夠同時操做同一個數據 2.同步監視器:俗稱"鎖",任何一個對象均可以充當。全部的線程都公用一個監視器。
實現代碼
class Window implements Runnable{ //總共的票數 //基於該對象的多個線程共享該資源 int ticket = 100; //同步監視器 //任何一個對象均可以充當監視器 Object oj = new Object(); @Override public void run() { while (true) { //同步 //oj也能夠寫成this,表示本類的實例對象充當監視器 synchronized (oj) { if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"售票,票號爲:"+ticket--); }else { break; } } } } }
實現原理:
監視器(oj)在同一時刻只放一個線程進入if(){}方法,該線程執行完任務退出後,再放其餘線程(這裏是全部的線程公平競爭,也包括剛剛執行過的線程)進入。所以,該監視器就像一把鎖,一旦有線程進入if()方法,便鎖住,不讓其餘線程進入;當該線程執行完退出後,監視器打開鎖,再放一個線程進來。
#####方法2:同步方法
將操做共享數據的方法聲明爲synchronized。即此方法爲同步方法,它可以保證一個線程在執行此方法時,其它線程在外等待直至該線程結束。同步方法的鎖即爲this。
代碼以下
class Window implements Runnable{ //總共的票數 //基於該對象的多個線程共享該資源 int ticket = 100; //同步監視器 //任何一個對象均可以充當監視器 Object oj = new Object(); @Override public void run() { while (true) { show(); } } //將共享的方法聲明爲同步方法 public synchronized void show() { if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"售票,票號爲:"+ticket--); } } }
###8.懶漢式單例模式線程安全
//懶漢式單例模式線程安全 class Singleton{ private Singleton(){ } private static Singleton instance = null; public static Singleton getInstance(){ if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
測試代碼
public static void main(String[] args) { Singleton singleton1 = Singleton.getInstance(); Singleton singleton2 = Singleton.getInstance(); System.out.println(singleton1 == singleton2); }
###9.線程同步練習
需求:銀行有一個帳戶,有兩個儲戶,同時向該帳戶存3000塊錢(每次存1000,分3次存完),每次存完錢都須要打印出該帳戶的餘額。
需求分析以下
/** * 銀行有一個帳戶 * 有兩個儲戶,同時向該帳戶存3000塊錢(每次存1000,分3次存完),每次存完錢都須要打印出該帳戶的餘額 * 預想結果: * 1.兩個儲戶交替存錢 * 2.帳戶的錢每次都加1000 * 分析: * 1.建立一個帳戶類 * 2.建立一個儲戶類(兩個儲戶能夠看做兩個線程) * */
Account類
class Account{ //帳戶餘額 private double balance; //存錢方法 public synchronized void storeMoney(double money){ balance = balance+money; try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+",帳戶餘額:"+balance); } }
Customer類
class Customer implements Runnable{ Account Account = new Account(); @Override public void run() { for (int i = 0; i < 3; i++) { Account.storeMoney(1000); } } }
測試代碼
public class Main { public static void main(String[] args) { Customer customer = new Customer(); Thread thread1 = new Thread(customer); thread1.setName("儲戶1"); Thread thread2 = new Thread(customer); thread2.setName("儲戶2"); thread1.start(); thread2.start(); } }
###10.線程死鎖
死鎖:不一樣的線程分別佔用對方須要的同步資源不放棄,都在等待對方優先釋放本身須要的同步資源,這樣就造成了線程的死鎖。死鎖示例:
public class Main { static StringBuffer sb1 = new StringBuffer(); static StringBuffer sb2 = new StringBuffer(); public static void main(String[] args) { //線程1 new Thread(){ @Override public void run() { synchronized (sb1) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } sb1.append("A"); synchronized (sb2) { sb2.append("B"); System.out.println(sb1); System.out.println(sb2); } } } }.start(); //線程2 new Thread(){ @Override public void run() { synchronized (sb2) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } sb2.append("C"); synchronized (sb1) { sb1.append("D"); System.out.println(sb1); System.out.println(sb2); } } } }.start(); } }
如上代碼就會出現死鎖現象,例如
線程1搶到資源sb1,而後休眠;在休眠期間,線程2搶到了資源sb2。此時線程1須要資源sb2,可是資源sb2被線程2搶佔着;同時線程2須要資源sb1,可是資源sb1被線程1搶佔着。線程1等着線程2釋放資源sb2,線程2等着線程1釋放資源sb1,線程1與線程2一直這樣僵持着,致使程序沒法運行下去,這就構成了死鎖。
###11.線程通訊
線程通訊中用到的三個方法:
wait():使當前線程掛起,並釋放CPU(同步資源),使其它線程能夠訪問並修改同步資源。
notify():喚醒正在排隊等待同步資源的線程(優先級最高的那一個),使其訪問/修改同步資源。
notifyall():喚醒正在排隊等待同步資源的全部線程。
如上三個方法是在java.lang.Object中提供的,且只能用在synchronized方法(或 synchronized代碼塊中)。
/** * 使用兩個線程交替打印輸出1-100 * */ class PrintNum implements Runnable { int num = 1; @Override public void run() { while (true) { this.show(); } } public synchronized void show() { //喚醒 notify(); if (num <= 100) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + num); num = num + 1; } try { //讓出資源 wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public class Main { public static void main(String[] args) { PrintNum printNum = new PrintNum(); Thread thread1 = new Thread(printNum); Thread thread2 = new Thread(printNum); thread1.setName("線程1"); thread2.setName("線程2"); thread1.start(); thread2.start(); } }