java學習筆記之初識多線程
初識多線程
一.進程的認識:
1.進程的理解:
進程可以理解成正在執行的任務,就是正在運行的程序,進程又分為兩種,一種是前臺進程,就是我們很直觀看見的,另一種是後臺進程,就是操作系統啟動就有的(系統級的進程),每個進程運行之後都會占用一定的cpu和內存資源;
比如說:我們打開window任務管理器就可以看到有很多的進程,裏面有用戶開啟的,也有操作系統啟動的進程
2.進程的調度:
也就是那麽多的進程,cpu是怎樣運行的呢?
采用時間片輪轉法
二.線程認識
1.線程的理解
線程是在進程的內部運行的執行體
(1).一個進程可以創建多個線程
(2).進程和線程共同來爭奪cpu,而且概率是均等的
(3).線程依賴進程,不能獨立存在
(4).在java中,一個進程至少有一個線程,main方法被稱為主線程;
我們可以在java的main方法中輸出一個1/0;
(5).進程或者線程都有優先級,優先級高的可以得到更多的時間片和cpu的執行權
(6).搶占式調度:優先級高的進程可以搶占優先級低的cpu執行權
2.為什麽要搞多線程?
原因:
(1).讓多個程序同時執行
(2).提高程序的執行效率
三.線程的創建
1.方式一:繼承Thread類
小demo:
1 //定義一個類,繼承Thread類 2 public classMyThread extends Thread { 3 4 //重寫Thread類的run方法 5 public void run(){ 6 System.out.print("我是一個子線程"); 7 } 8 }
1 public class Demo { 2 public static void main(String[] args) { 3 //創建子類對象,然後調用start();方法 4 MyThread mt=new MyThread(); 5 mt.start();6 } 7 }
2.方式二:實現Runnable接口
小demo:
//實現類的定義 public class MyRunnableImpl implements Runnable { //定義一個Runnable實現類,然後重寫run方法 @Override public void run() { //這裏寫功能代碼 } }
1 public class Demo { 2 //使用代碼 3 public static void main(String[] args) { 4 //new 一個Runnable實現類對象 5 MyRunnableImpl my=new MyRunnableImpl(); 6 //然後將該實現類對象,傳入Thread類的構造方法中,並創建Thread類對象 7 //調用Thread對象start()方法開啟線程 8 new Thread(my).start(); 9 } 10 }
四.線程的安全
1.線程安全問題產生的原因:
當多個線程去訪問共享資源時候,就會發生線程安全問題
先看一個案例:多窗口賣電影票的案例
1 class Ticket implements Runnable { 2 //電影票的數量 3 private static int ticket=100; 4 @Override 5 public void run() { 6 while(true){ 7 if(ticket>0){ 8 //模擬網絡延時的場景 9 try { 10 Thread.sleep(50); 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 } 15 //對票數進行出售(減減操作) 16 System.out.println("正在售出第"+ticket--+"張票"); 17 } 18 } 19 } 20 } 21 public class Demo { 22 public static void main(String[] args) { 23 //new 一個實現類對象 24 Ticket t=new Ticket(); 25 //創建3個線程對電影票進行售賣 26 new Thread(t).start(); 27 new Thread(t).start(); 28 new Thread(t).start(); 29 30 } 31 }
這時候就出現了問題:
出現這個問題的解釋:
當擁還剩一張電影票的時候,擁有cpu執行權的線程運行到while(true)的時候,順利通過,然後在運行sleep();這時候這個線程進入堵塞狀態,電影票還是1,cpu的執行權被另一個線程拿到,也正好運行到while(true),順利通過,然後在運行sleep();cpu執行權被第三個線程搶去,同樣的運行,到最後醒過來的線程會繼續執行下面的代碼。就產生了負數票的現象
2.線程安全問題的解決方案:
(1).同步代碼塊
1 2 //定義一個對象,當做鎖對象 3 static Object obj = new Object(); 4 /* 5 同步代碼塊: 6 參數:鎖對象 7 1:java任何對象都可以作為鎖對象 8 2:所有的線程都必須共享這個鎖對象(該對象只有一份) 9 synchronized(mutex){ 10 //存放需要加鎖的代碼 11 } 12 */ 13 14 //實現賣票的代碼 15 @Override 16 public void run() { 17 18 while(true){ 19 synchronized (obj) {//加鎖 20 if(tickets > 0 ){ 21 //模擬一個網絡的延時 22 try{Thread.sleep(50);}catch(Exception e){} 23 System.out.println("正在出售第"+ tickets-- + "張票"); 24 } 25 }//解鎖 26 } 27 }
(2).同步方法
1 /* 2 * 同步方法:對整個方法加鎖 3 * 4 * 1:同步方法的鎖對象是this, 這裏的this必須是同一個 5 * 2:靜態方法的鎖對象是:類名.class 6 */ 7 /* 8 * 同步代碼塊和同步方法的區別: 9 * 1:同步代碼塊可以對任何一段代碼進行加鎖 10 * 2:同步方法是對整個方法都進行加鎖 11 * 12 * 13 * 3:同步代碼塊的對象可以自己指定 14 * 4:同步方法的鎖對象必須是this 15 */ 16 public synchronized void method(){ 17 if (tickets > 0) { 18 // 模擬一個網絡的延時 19 try { 20 Thread.sleep(50); 21 } catch (Exception e) { 22 } 23 System.out.println("正在出售第" + tickets-- + "張票"); 24 } 25 } 26 27 // 實現賣票的代碼 28 @Override 29 public void run() { 30 31 while (true) { 32 method(); 33 } 34 }
(3).同步鎖
1 /* 2 * 同步鎖: 3 * Lock接口: 4 * ReentrantLock實現類: 5 * lock(); 加鎖 6 * unlock();解鎖 7 */ 8 9 //1:創建鎖對象 10 ReentrantLock rt = new ReentrantLock(); 11 12 13 // 實現賣票的代碼 14 @Override 15 public void run() { 16 17 while (true) { 18 //2:在適當的地方加鎖 19 rt.lock(); 20 21 if (tickets > 0) { 22 // 模擬一個網絡的延時 23 try { 24 Thread.sleep(50); 25 } catch (Exception e) { 26 } 27 System.out.println("正在出售第" + tickets-- + "張票"); 28 } 29 //3:在適當的地方解鎖 30 rt.unlock(); 31 } 32 }
五.線程的狀態
java學習筆記之初識多線程