程序(軟件):數據和指令的集合。軟件架構:B/S C/S 軟件分類:系統軟件,應用軟件。
進程:正在運行的程序,會在內存中分配空間。
線程:進程中的多條路徑。
多線程是指有多條線程並發的執行。
並發:多條線程在同一時間段內交替執行。
並行:多條線程同時執行。
創建線程:
1.繼承:extends Thread類 繼承的子類需要重寫run方法
2.實現:implements Runnable接口
注意:無論是繼承還是實現,直接調用重寫過的run方法是無法開啟多線程的,jvm中默認start()方法開啟多線程,start()方法會默認調用重寫的run()方法。
3.線程創建方式的優缺點:
① 繼承方式寫法簡單。實現方式寫法相對比較復雜
② 繼承與實現都是通過 start 方法才能開啟線程。
③ 繼承的擴展性和靈活性都低於實現方式的擴展性和靈活性
④ 繼承的方式,耦合度高於實現的方式。
實際開發中,使用實現形式要多於繼承形式。
使用繼承來開啟多線程 :代碼示例:
1 package com.Allen.Thread; 2 3 public class Demo5 { 4 public static void main(String[] args) { 5 ExtendsThread et=new ExtendsThread(); 6 et.start(); 7 for(int j=0;j<100;j++) { 8 System.out.println("=========j"+j); 9 } 10 } 11 } 12 class ExtendsThread extends Thread{ 13 @Override 14 public void run() { 15 for (int i = 65; i <91; i++) { 16 System.out.println((char)i); 17 } 18 } 19 }
執行結果:

可以看到兩個線程在交替運行。
使用實現Runnable接口來開啟多線程:代碼示例:
1 package com.Allen.Thread; 2 3 public class Demo6 { 4 5 public static void main(String[] args) { 6 ImplementsRunnable ir=new ImplementsRunnable(); 7 Thread td=new Thread(ir); 8 td.start(); 9 for (int j = 0; j < 1000; j++) { 10 System.out.println("====j"+j); 11 } 12 } 13 } 14 class ImplementsRunnable implements Runnable{ 15 16 @Override 17 public void run() { 18 for (int i = 0; i < 1000; i++) { 19 20 System.out.println("***********i"+i); 21 } 22 } 23 }
執行結果:

實現Runnable接口來開啟多線程還可以使用匿名對象 代碼示例:
1 package com.Allen.Thread; 2 3 public class Demo6 { 4 5 public static void main(String[] args) { 6 7 Thread td=new Thread(new ImplementsRunnable()); //這里使用的是匿名對象 8 td.start(); 9 for (int j = 0; j < 1000; j++) { 10 System.out.println("====j"+j); 11 } 12 } 13 } 14 class ImplementsRunnable implements Runnable{ 15 16 @Override 17 public void run() { 18 for (int i = 0; i < 1000; i++) { 19 20 System.out.println("***********i"+i); 21 } 22 } 23 }
線程的執行原理:
線程的執行:CPU的搶占式的調度模式,搶占CPU的時間片

不同的線程不共享棧空間,需要開辟棧空間單獨運行
線程的生命周期:
***1. 線程的生命周期 / 線程的狀態
① 生命周期:從事物的出生,到消亡的過程。
***② 線程生命周期的狀態:
1 》初始狀態 : 創建了線程對象,但是沒有調用 start 方法開啟線程。
2 》可運行狀態: 調用了 start 方法,到線程隊列中排隊,搶占 cpu 的時間片。但是還沒有搶占上
。
3 》運行狀態: 搶到了 cpu 的時間片。正在執行。
4 》 終止狀態: cpu 時間片之內完成了線程的執行。
5 》 阻塞狀態: cpu 時間片執行期間,遇到了意外的情況。
③ 線程的生命周期,指同一個線程在不同時期的狀態。
④ java 中,線程的生命周期通過類來表示不同的狀態 : Thread.State
new
至今尚未啟動的線程處於這種狀態。
runnable
正在 Java 虛擬機中執行的線程處於這種狀態。
blocked
受阻塞並等待某個監視器鎖的線程處於這種狀態。
waiting
無限期地等待另一個線程來執行某一特定操作的線程處於這種狀態。
timed_waiting
等待另一個線程來執行取決於指定等待時間的操作的線程處於這種狀態。
terminated
已退出的線程處於這種狀態。

線程的常用方法;
1.getName()返回線程的名稱
2.currentThread()獲取當前線程的引用對象。
線程的名稱 main方法所在的線程為主線程 ,線程名為main
自定義的線程:Thread-0,Thread-1 默認格式:Thread-index 其中index從0開始 線程的名稱可以設置
3.setName () 設置線程的名稱
4.***getpriority()/setPriority() 返回線程的優先級/設置線程的優先級
***線程的優先級:從1到10,默認為5,最高優先級為10,最低優先級為1
線程如果優先級越高,搶占CPU時間片的可能性越大,默認一個線程創建時,優先級為5.
5.isDaemon()/setDeamon(true) 判斷該線程是否為守護線程/設置線程為守護線程,參數值為true。
守護線程是用來守護用戶線程,為用戶線程進行准備或者守護的工作。
隨着用戶線程的消亡,守護線程無論是否執行完都會隨着用戶線程消亡。
6.join()等待該線程的終止,相當於用戶調用。
7****sleep(ms) 休眠,毫秒值
8.start()開啟線程的方法,會默認調用run()方法,進行線程的開啟。
9.yield()退出當前正在執行的線程,讓給其他線程執行,線程的讓步。
守護線程的代碼示例:
1 package com.Allen.Thread; 2 3 public class Demo7 { 4 5 public static void main(String[] args) { 6 DaemonThread dh=new DaemonThread(); 7 dh.setDaemon(true); 8 dh.start(); 9 for (int j = 0; j <20; j++) { 10 System.out.println("****"+j); 11 } 12 } 13 14 } 15 class DaemonThread extends Thread{ 16 @Override 17 public void run() { 18 for (int i = 0; i < 100; i++) { 19 20 System.out.println("=================="+i); 21 } 22 } 23 }
執行結果:

本例DaemonThread線程要打印倒99,但是隨着主線程的消亡,守護線程也消亡了,但是為什么主線程 到19就消亡了,而守護線程卻到了28呢?這是因為有延遲,不能立即結束。
主線程和守護線程的關系就像人與影子的關系,共存亡。
線程的安全問題:
1.多線程:多個線程並發執行。
2.多線程在執行時,會引發安全問題
3. 通過銀行賬戶取款的模擬來演示多線程的安全問題。
4. 多線程的安全問題:
多個線程共同訪問共享資源 ( 共有的資源 ) 時,由於線程的調度機制是搶占式調度,可能會發生多個線程在執行時,
同時操作共享資源,導致程序執行結果與預期不一致的現象。
5. 解決多線程的安全問題:
1> 解決方式一:同步:但是該方式會導致效率低,相當於單個線程執行。
a) 同步方式: ( 加鎖 )
① 同步方法 : 相當於同步代碼塊中的 this 鎖。
格式 :
權限修飾符 修飾符 synchronized 方法的返回值 方法的名稱 ( 參數 )
② 同步代碼塊:
1 》 同步鎖對象 :任意對象
格式 : synchronized ( 任意對象 ){
加鎖的代碼
}
2 》同步 this 鎖:
③ 同步代碼塊和同步方法加鎖時,需要注意:
《 1 》 加鎖必須是給共享資源加的。
《 2 》 必須加的是同一把鎖。
多線程安全問題的代碼示例:
1 package com; 2 /** 3 1.多線程 : 多個線程並發執行。 4 2.多線程在執行時,會引發安全問題。 5 3.通過銀行賬戶取款的模擬來演示多線程的安全問題。 6 4.多線程的安全問題: 7 多個線程共同訪問共享資源(共有的資源)時,由於線程的調度機制是搶占式調度,可能會發生多個線程在執行時, 8 同時操作共享資源,導致程序執行結果與預期不一致的現象。 9 5.解決多線程的安全問題: 10 1>解決方式一:同步:但是該方式會導致效率低,相當於單個線程執行。 11 a)同步方式:(加鎖) 12 ① 同步方法: 相當於同步代碼塊中的this鎖。 13 格式 : 14 權限修飾符 修飾符 synchronized 方法的返回值 方法的名稱(參數) 15 ② 同步代碼塊: 16 1》 同步鎖對象 :任意對象 17 格式 :synchronized (任意對象){ 18 加鎖的代碼 19 } 20 2》同步this鎖: 21 ③ 同步代碼塊和同步方法加鎖時,需要注意: 22 《1》 加鎖必須是給共享資源加的。 23 《2》 必須加的是同一把鎖。 24 */ 25 public class 多線程的安全問題 { 26 public static void main(String[] args) { 27 Account a=new Account(); 28 //多線程 29 Thread t=new Thread(a); 30 Thread t1=new Thread(a); 31 t.setName("張三"); 32 t1.setName("李四"); 33 t.start(); 34 t1.start(); 35 } 36 } 37 class Account implements Runnable{ 38 double balance=1000;//賬戶余額 39 Object obj=new Object();//任意對象 40 String s=new String(); 41 //同步方法 42 @Override 43 public /*synchronized*/ void run() { 44 //同步鎖對象,任意對象。 45 synchronized (this) {//this鎖 46 //取款 47 if (balance > 0) { 48 //休眠 49 try { 50 Thread.sleep(1000); 51 } catch (InterruptedException e) { 52 // TODO Auto-generated catch block 53 e.printStackTrace(); 54 } 55 balance -= 1000; 56 System.out.println(Thread.currentThread().getName() + "取款后,金額為" + balance); 57 } else { 58 System.out.println(Thread.currentThread().getName() + "余額不足"); 59 } 60 } 61 } 62 }
======================================================================
1 package com; 2 /** 3 4 1.多線程的安全問題: ① 多個線程 ② 共享資源。 5 2.同步: 判斷多個線程是否加的是同一把鎖。 6 ① 同步方法 7 ② 同步代碼塊: 8 1》 鎖對象(任意對象) 9 2》this鎖 10 3》靜態鎖 (靜態對象) 11 4》類鎖(.class) 12 */ 13 public class 繼承方式下的多線程的安全問題 { 14 public static void main(String[] args) { 15 AccountThread a=new AccountThread(); 16 AccountThread a1=new AccountThread(); 17 a.setName("張三"); 18 a1.setName("李四"); 19 a.start(); 20 a1.start(); 21 22 } 23 } 24 class AccountThread extends Thread{ 25 static double balance=1000;//賬戶余額 26 //靜態鎖 27 static Object o=new Object(); 28 //同步方法 29 @Override 30 public void run() { 31 //類鎖 32 synchronized (AccountThread.class) {//this 33 //取款 34 if (balance > 0) { 35 //休眠 36 try { 37 Thread.sleep(1000); 38 } catch (InterruptedException e) { 39 // TODO Auto-generated catch block 40 e.printStackTrace(); 41 } 42 balance -= 1000; 43 System.out.println(Thread.currentThread().getName() + "取款后,金額為" + balance); 44 } else { 45 System.out.println(Thread.currentThread().getName() + "余額不足"); 46 } 47 } 48 } 49 }
PS:alt+方向鍵可以上下平移代碼
ctrl+加減號可以調節eclipse的字體大小
shift+ctrl+delete 刪除整行
1 package com.Allen.Thread; 2 public class Demo8 { 3 public static void main(String[] args) { 4 CountMoney td1=new CountMoney(); 5 CountMoney td2=new CountMoney(); 6 td1.setName("張先生"); 7 td2.setName("李先生"); 8 td1.start(); 9 td2.start(); 10 } 11 } 12 class CountMoney extends Thread{ 13 static double count=1000; 14 static Object o=new Object(); 15 public void run() { 16 synchronized(o) { 17 if(count>0) { 18 try { 19 Thread.sleep(1000); 20 } catch (InterruptedException e) { 21 // TODO Auto-generated catch block 22 e.printStackTrace(); 23 } 24 count-=1000; 25 System.out.println(Thread.currentThread().getName()+"取款成功,余額:"+count); 26 }else { 27 System.out.println(Thread.currentThread().getName()+"余額不足"); 28 } 29 } 30 } 31 32 }
