主線程:執行主方法的線程,就叫做主線程 單線程程式:程式從mani開始從上到下依次運行 程式從main方法開始運行,JVM運行main方法,會找操作系統 開闢一條通向cpu的執行路徑,cpu可以通過這條路徑來執行main方法 這條路徑有一個名字叫主(main)線程 創建線程方式一繼承Thread類 ...
主線程:執行主方法的線程,就叫做主線程
單線程程式:程式從mani開始從上到下依次運行
程式從main方法開始運行,JVM運行main方法,會找操作系統
開闢一條通向cpu的執行路徑,cpu可以通過這條路徑來執行main方法
這條路徑有一個名字叫主(main)線程
創建線程方式一繼承Thread類
實現步驟:
1.創建Thread類的子類
2.重寫Thread類中的run方法,設置線程的任務
3.創建Thread類的子類對象
4.調用Thread類中的start方法開啟一個新的線程,執行run方法
使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
結果是兩個線程併發地運行;當前線程(main線程)和另一個線程(執行 run 方法的線程)。
多次啟動一個線程是非法的。特別是當線程已經結束執行後,不能再重新啟動。
列印的結果出現了隨機性:
開啟兩個線程,對於cpu就選擇權利
喜歡誰,就執行誰,所以就出現了隨機性結果
1 public class MyThread extends Thread{ 2 /* 3 * 2.重寫Thread類中的run方法,設置線程的任務 4 * 開啟這個線程要乾什麼事情 5 */ 6 @Override 7 public void run() { 8 for (int i = 0; i < 50; i++) { 9 System.out.println("run..."+i); 10 } 11 } 12 } 13 public static void main(String[] args) { 14 //3.創建Thread類的子類對象 15 MyThread mt = new MyThread(); 16 //mt.run();//不會開啟線程,還是單線程程式 17 //4.調用Thread類中的start方法開啟一個新的線程,執行run方法 18 mt.start(); 19 20 new MyThread().start(); 21 22 for (int i = 0; i < 50; i++) { 23 System.out.println("main..."+i); 24 } 25 }
線程的名稱:
主線程:"main"
開啟的其它線程的名稱:"Thread-0","Thread-1"....
獲取線程的名稱
1.Thread類中的方法getName
String getName() 返回該線程的名稱。
2.Thread類中的靜態方法,獲取當前正在執行的線程
static Thread currentThread() 返回對當前正在執行的線程對象的引用。
設置線程的名稱:
1.Thread類中的方法setName(String name)
void setName(String name) 改變線程名稱,使之與參數 name 相同。
2.子類添加帶參構造,調用父類Thread類的帶參構造方法,傳遞線程的名稱,讓父類給線程起名字(讓父親給兒子起名字)
Thread(String name) 分配新的 Thread 對象。
創建線程方式—實現Runnable介面
實現步驟:
1.創建Runnable介面的實現類
2.重寫Runnable介面中的run方法,設置線程任務
3.創建Runnable介面的實現類對象
4.創建Thread類對象,構造方法中傳入Runnable介面的實現類
Thread(Runnable target) 分配新的 Thread 對象。
5.調用Thread類中的方法start,開啟線程執行run方法
實現Runnable的好處
1.避免了類繼承Thread類之後,無法繼承其它的類(單繼承的局限性)
2.把設置線程任務,和開啟線程進行解耦,增強了擴展性
實現類的作用:就是設置線程任務
Thread類的作用:開啟線程
好處:傳遞不同的實現類,實現類重寫的方法不一樣,可以調用不同的方法
線程的匿名內部類使用
匿名:沒有名字
內部類:寫在其他類內部的類(成員位置:成員內部類,局部位置(方法中):局部內部類)
匿名內部類的格式:
new 父類/介面(){
重寫父類/介面中的方法;
};
多線程的父類:
Thread
Runnable
1 new Thread(){ 2 //重寫run方法,設置線程任務 3 @Override 4 public void run() { 5 for (int i = 0; i < 20; i++) { 6 System.out.println(Thread.currentThread().getName()+":"+i); 7 } 8 } 9 }
以上一堆代碼就是一個創建子類重寫父類方法的過程
相當於: new MyThread().start();
程式出現了線程安全問題:賣了重覆的票和不存在的票
解決方案:
第一種方式:可以使用同步代碼塊
synchronized(鎖對象){
產生安全問題的代碼;
訪問了共用數據的代碼;
}
註意:
必須要保證多個線程使用的是同一個鎖對象
//在成員位置上創建一個鎖對象(保證唯一)
1 Object obj = new Object(); 2 3 @Override 4 public void run() { 5 //讓賣票重覆執行 6 while(true){ 7 8 * 同步代碼塊 9 * 程式會頻繁的判斷鎖,獲取鎖,釋放鎖,所以會降低速度 10 11 synchronized (obj) { 12 if(ticket>0){ 13 //為了提高安全問題的概率,讓程式睡眠 14 try { 15 Thread.sleep(10); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 //賣票ticket-- 20 System.out.println(Thread.currentThread().getName()+"...賣第"+ticket--+"張票"); 21 } 22 } 23 } 24 }
程式出現了線程安全問題:賣了重覆的票和不存在的票
解決方案:
第二種方式:同步方法
使用步驟:
1.把可能出現安全問題的代碼抽取到一個方法中
2.把方法增加一個關鍵字synchronized
修飾符 synchronized 返回值類型 方法名(參數){
可能出現安全問題的代碼;
訪問了共用數據的代碼;
}
同步方法使用的鎖對象是什麼?
使用的就是本類對象new RunnableImpl()-->叫this
靜態的同步方法,使用時什麼鎖對象?
使用的是本類對象的class屬性(反射)
RunnableImpl.class
1 *@Override 2 public void run() { 3 //讓賣票重覆執行 4 while(true){ 5 payTicket2(); 6 } 7 } 8 9 10 * 靜態的同步方法 11 12 public static synchronized void payTicket2(){ 13 synchronized (RunnableImpl.class) { 14 if(ticket>0){ 15 //為了提高安全問題的概率,讓程式睡眠 16 try { 17 Thread.sleep(10); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 //賣票ticket-- 22 System.out.println(Thread.currentThread().getName()+"...賣第"+ticket--+"張票"); 23 } 24 } 25 } 26 27 28 29 * 抽取出一個同步方法 30 * 快捷鍵:alt+shift+m 31 32 public ynchronized void payTicket1() { 33 synchronized (this) { 34 //System.out.println(this);//cn.itcsat.demo10.RunnableImpl@67064 35 if(ticket>0){ 36 //為了提高安全問題的概率,讓程式睡眠 37 try { 38 Thread.sleep(10); 39 } catch (InterruptedException e) { 40 e.printStackTrace(); 41 } 42 //賣票ticket-- 43 System.out.println(Thread.currentThread().getName()+"...賣第"+ticket--+"張票"); 44 } 45 } 46 }
程式出現了線程安全問題:賣了重覆的票和不存在的票
*
* 解決方案:
* 第三種方式:使用Lock介面,JDK1.5之後出現的
*
* java.util.concurrent.locks.Lock介面
* 方法:
* void lock() 獲取鎖。
* void unlock() 釋放鎖。
* 介面的實現類:ReentrantLock
*
* 實現步驟:
* 1.在成員位置創建一個Lock介面的實現類對象ReentrantLock
* 2.在可能出現線程安全問題的代碼前,調用lock方法獲取鎖
* 3.在可能出現線程安全問題的代碼後,調用unlock方法釋放鎖
*
*1.在成員位置創建一個Lock介面的實現類對象ReentrantLock
Lock l = new ReentrantLock();
1 @Override 2 public void run() { 3 //讓賣票重覆執行 4 while(true){ 5 //2.在可能出現線程安全問題的代碼前,調用lock方法獲取鎖 6 l.lock(); 7 if(ticket>0){ 8 //為了提高安全問題的概率,讓程式睡眠 9 try { 10 Thread.sleep(10); 11 //賣票ticket-- 12 System.out.println(Thread.currentThread().getName()+"...賣第"+ticket--+"張票"); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 }finally { 16 //3.在可能出現線程安全問題的代碼後,調用unlock方法釋放鎖 17 l.unlock(); 18 } 19 } 20 }