Java零基礎之多執行緒part2(新手可看)
Part2
執行緒的生命週期
- 新建
- 就緒
- 執行
- 阻塞
- 死亡
執行緒的同步
生活例項:廁所存在X一個坑位
正常情況下多執行緒的執行方式為:
A進入 X坑位----執行pullshit( )----出坑----→下一個物件進X坑。。。。。
但是不安全的多執行緒可能會出現:
A進入坑位==執行pullshit( )【未出坑】—B很著急,進入X坑–同時pullshit
此時即為執行緒不安全,可能會造成髒資料等情況
【解決方案】:加鎖
Java中解決方法:同步鎖機制,解決執行緒安全
同步程式碼塊
synchronized(同步監視器){
//需要同步的程式碼
說明:
操作共享資料的程式碼,即需要被同步的程式碼
【注意同步、的範圍】
共享資料:多個執行緒共同操作的變數:比如上文的ticnum
同步監視器:俗稱—鎖:任何一個類的物件都可以當鎖
要求多個執行緒必須共用同一把鎖
Runnable中考慮 this充當同步鎖
繼承Thread類中,慎用 this
考慮當前類.class充當同步鎖
}
public class SynchronizedDemo { public static void main(String[] args) { TicketAdd ticketAdd = new TicketAdd(); Thread t1 = new Thread(ticketAdd); Thread t2 = new Thread(ticketAdd); t1.setName("Thread==1:"); t2.setName("Thread==2:"); t1.start(); t2.start(); } } /* * 為何不將同步鎖把while(true)也包含在內 * 個人理解: * 在不將while包括在鎖內的時候,兩個執行緒在呼叫的時候 具體看cpu資源分配,可能執行執行緒1 ,也可能執行執行緒2 * 但是都只會執行一次其中的判斷,要麼賣出一張票,要麼無票,若有票,繼續看cpu執行權 * * 然而將while包含在內的時候,如果先start執行緒一,那麼此時,執行緒同步鎖發揮作用,會一直將執行緒一執行,直到沒有票break了,才會去執行執行緒二,但是此時執行緒2 已經沒票了 * */ class TicketAdd implements Runnable{ private int ticnum = 10000; Object object = new Object(); @Override public void run() { while (true){ synchronized(object){ if (ticnum>0){ System.out.println(Thread.currentThread().getName()+"sold:"+ticnum); ticnum--; }else { System.out.println(Thread.currentThread().getName()+"沒票了"); break; } } } } }
同步方法
如果操作共享資料的程式碼完整的宣告在同一個方法中
不妨將此方法宣告為同步
- Runnable
public class window2 { public static void main(String[] args) { windows w1 = new windows(); Thread t1 = new Thread(w1); Thread t2 = new Thread(w1); t1.setName("Thread==1:"); t2.setName("Thread==2:"); t1.start(); t2.start(); } } class windows implements Runnable{ private int ticnum = 10000; @Override public void run() { while (true){ sold(); } } private synchronized void sold(){//採用方法同步鎖 ==此時監視器為this,即當前物件 if (ticnum>0){ System.out.println(Thread.currentThread().getName()+"sold:"+ticnum); ticnum--; }else { System.out.println(Thread.currentThread().getName()+"沒票了"); System.exit(1); } } }
- Extends Thread
public class window3 { public static void main(String[] args) { windowExtends w1 =new windowExtends(); windowExtends w2 =new windowExtends(); w1.setName("W1:"); w2.setName("W2:"); w1.start(); w2.start(); } } class windowExtends extends Thread{ private static int ticnum = 1000; public void run() { while (true){ sold2(); } } //此時同步監視器為windowExtedns.class private static synchronized void sold2(){ if (ticnum>0){ System.out.println(Thread.currentThread().getName()+"sold:"+ticnum); ticnum--; }else { System.out.println(Thread.currentThread().getName()+"沒票了"); System.exit(1); } } }
總結 :
同步方法仍然涉及到同步監視器,但是不需要顯式宣告
非靜態同步方法,同步監視器為:this
上述Runnable介面實現的就是非靜態同步方法
靜態的同步方法,同步監視器為:當前類本身
上述Extedns Thread實現的就是靜態同步方法
死鎖
死鎖的理解:
解決方法:
Lock鎖–ReentranLock–
解決執行緒安全的方法三
Lock介面下的ReentranLock
class Rdemo implements Runnable{ private ReentrantLock lock = new ReentrantLock();//建立一個鎖 @Override public void run() { try { lock.lock();//上鎖,後續的程式碼都是鎖定的 System.out.println("helo"); }finally { lock.unlock();//解鎖 } } }
synchronized和ReentranLocked的不同點
- lock手動鎖定和解鎖
- synchronized自動解鎖
優先使用順序:
執行緒通訊
- wait():執行此方法,當前執行緒就會進入阻塞狀態,並釋放同步監視器
- notify():喚醒另一個被wait的執行緒
- notifyAll():喚醒其他所有wait的執行緒
sleep和wait方法的異同
-
同:都可以讓當前執行緒進入阻塞狀態
-
異:
-
宣告位置不同
sleep在Thread類中宣告
wait在Object類中宣告
-
呼叫範圍不同
sleep可以在任何情況下使用
wait只能在同步程式碼塊或者同步方法中使用
-
是否釋放同步監視器
當他們都在同步程式碼塊或者同步方法中使用時,sleep不釋放同步監視器
-
生產消費者模型
存在兩個執行緒 生產和消費
可能出現的問題:生產或消費的阻塞期,產生髒資料
程式碼實現Version1
public class PandC2 { public static void main(String[] args) { Clerk2a clerk2a = new Clerk2a(); Customer1 c1 = new Customer1(clerk2a); Customer2 c2 = new Customer2(clerk2a); Productor1 p1 = new Productor1(clerk2a); Thread t1 = new Thread(c1); Thread t12 = new Thread(c2); Thread t2 = new Thread(p1); t1.setName("消費者執行緒"); t12.setName("消費者執行緒2"); t2.setName("生產者執行緒"); t1.start(); t12.start(); t2.start(); } } class Clerk2a{ private int num = 0; public synchronized void shengchan(){ if (num<20){ num++; System.out.println(Thread.currentThread().getName()+"生產第"+num+"個產品"); notify(); }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void xiaofei(){ if (num>0){ System.out.println(Thread.currentThread().getName()+"消費第"+num+"個產品"); num--; notify(); }else { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Productor1 implements Runnable{ private Clerk2a clerk2a; public Productor1(Clerk2a clerk2a) { this.clerk2a = clerk2a; } @Override public void run() { while (true){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } clerk2a.shengchan(); } } } class Customer1 implements Runnable{ private Clerk2a clerk2a; public Customer1(Clerk2a clerk2a) { this.clerk2a = clerk2a; } @Override public void run() { while (true){ try { Thread.sleep(150); } catch (InterruptedException e) { e.printStackTrace(); } clerk2a.xiaofei(); } } } class Customer2 implements Runnable{ private Clerk2a clerk2a; public Customer2(Clerk2a clerk2a) { this.clerk2a = clerk2a; } @Override public void run() { while (true){ clerk2a.xiaofei(); } } }
JDK5 新增的兩種建立多執行緒方式
實現Callable介面
相比於Runnable介面的優勢
有返回值
可以丟擲異常
支援泛型
/* * 1. 建立一個實現Callable介面的類A * 2. 重寫cal方法 * 3. 建立Callable介面實現類A的物件 * 4. 將A的物件作為引數傳遞到FutureTask作為引數生成一個新的FutureTask物件B * 5. 同理B作為引數生成Thread類物件,並呼叫start方法 * */
public class MulThreadPool { public static void main(String[] args) { A a = new A(); FutureTask futureTask = new FutureTask(a); Thread t1 = new Thread(futureTask); t1.setName("cal執行緒"); t1.start(); } } class A implements Callable{ @Override public Object call() throws Exception { System.out.println("hello world"); return null; } }
使用執行緒池
ExecutorService 是真正的執行緒池介面,但由於他只是介面
需要通過Executors工廠類的方法來決定建立不同的具體物件
工廠類方法包括(自行查閱API檢視具體,只描述了最常用的方法)
Executors.newCachedThreadPool( )
Executors.newFixedThreadPool( )
建立可重複固定執行緒數的執行緒池
Executors.newSingleThreadPool( )
Executors.newScheduledThreadPool( )
public class MulThreadPool{ public static void main(String[] args) { //1. 根據需求建立執行緒池 ExecutorService service = Executors.newFixedThreadPool(5); System.out.println(service.getClass()); //設定執行緒池的屬性 ThreadPoolExecutor tpe = (ThreadPoolExecutor) service; // 2. 執行指定的執行緒操作,需要提供實現了Runnable介面或者Callable介面的實現類的物件 service.execute(new N()); service.execute(new M()); // 3. 關閉執行緒池 service.shutdown(); } } class M implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"hello world"); } } class N implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"hello world"); } }
如有任何疑問,歡迎留言
相關文章
- Java基礎之多執行緒程式設計Java執行緒程式設計
- Java必會之多執行緒Java執行緒
- 玩轉java多執行緒 之多執行緒基礎 執行緒狀態 及執行緒停止實戰Java執行緒
- python之多執行緒Python執行緒
- C#基礎之多執行緒講解C#執行緒
- 【Java面試題】Java面試之多執行緒!Java面試題執行緒
- Java面試之多執行緒&併發篇Java面試執行緒
- 併發程式設計之多執行緒基礎程式設計執行緒
- Java面試之多執行緒&併發篇(2)Java面試執行緒
- Java面試之多執行緒&併發篇(5)Java面試執行緒
- Java面試之多執行緒&併發篇(3)Java面試執行緒
- Java面試之多執行緒&併發篇(4)Java面試執行緒
- Java面試之多執行緒&併發篇(6)Java面試執行緒
- Java面試之多執行緒&併發篇(8)Java面試執行緒
- Java面試之多執行緒&併發篇(9)Java面試執行緒
- Java面試之多執行緒&併發篇(7)Java面試執行緒
- Java執行緒池一:執行緒基礎Java執行緒
- python之多執行緒(學習)Python執行緒
- 併發程式設計之多執行緒執行緒安全程式設計執行緒
- Java 執行緒基礎Java執行緒
- Java 多執行緒基礎(四)執行緒安全Java執行緒
- Python筆記二之多執行緒Python筆記執行緒
- JUC之多執行緒鎖問題執行緒
- Java 多執行緒基礎(八)執行緒讓步Java執行緒
- java - 多執行緒基礎Java執行緒
- Java多執行緒-基礎篇Java執行緒
- Java 基礎(十四)執行緒——下Java執行緒
- Java 多執行緒基礎 - CyclicBarrierJava執行緒
- Java基礎之執行緒安全Java執行緒
- [Java基礎]虛擬執行緒Java執行緒
- Java 多執行緒基礎(六)執行緒等待與喚醒Java執行緒
- Java 多執行緒基礎(十一)執行緒優先順序和守護執行緒Java執行緒
- 突擊檢查:Java面試之多執行緒&併發篇(11)Java面試執行緒
- 突擊檢查:Java面試之多執行緒&併發篇(10)Java面試執行緒
- JavaSE高階程式設計之多執行緒Java程式設計執行緒
- Java基礎之執行緒那些事Java執行緒
- Java-基礎-執行緒入門Java執行緒
- java基礎 關於執行緒安全Java執行緒