62_Java_多線程2_線程生命周期_線程安全
線程的生命周期
新建: 當一個Thread類或其子類的對象被聲明并創建時,新生的線程對象處于新建 狀態
就緒:處于新建狀態的線程被start()后,將進入線程隊列等待CPU時間片,此時它已 具備了運行的條件,只是沒分配到CPU資源
運行:當就緒的線程被調度并獲得CPU資源時,便進入運行狀態, run()方法定義了線 程的操作和功能
阻塞:在某種特殊情況下,被人為掛起或執行輸入輸出操作時,讓出 CPU 并臨時中 止自己的執行,進入阻塞狀態
死亡:線程完成了它的全部工作或線程被提前強制性地中止或出現異常導致結
多線程出現了安全問題
當多條語句在操作同一個線程共享數據時,一個線程對多條語句只執行了一部分,還沒有執行完,另一個線程參與進來執行。
導致共享數據的錯誤
解決方式: 對多條操作共享數據的語句,只能讓一個線程都執行完,在執行過程中,其他線程不可以參與執行
線程的同步-解決線程安全問題-我們通過同步機制,來解決線程的安全問題
方式一:同步代碼塊
方式二:同步方法
方式三:Lock鎖
方式一:同步代碼塊
synchronized(同步監視器){? #?在繼承Thread類創建多線程的方式中,慎用this充當同步監視器,考慮使用當前類充當同步監視器
//需要被同步的代碼
}
說明: 1.操作共享數據的代碼,即為需要被同步的代碼。-->不能包含代碼多了,也不能包含代碼少了。
2.共享數據:多個線程共同操作的變量。比如:ticket就是共享數據。
3.同步監視器,俗稱:鎖。任何一個類的對象,都可以充當鎖。要求:多個線程必須要共用同一把鎖。
補充:在實現Runnable接口創建多線程的方式中,我們可以考慮使用this充當同步監視器。
class Window1 implements Runnable{ private int ticket = 100; @Override public void run() { while(true){ synchronized (this){ // 此時的this:唯一的Window1的對象 //方式二:synchronized (dog) { if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket); ticket--; } else { break; } public class WindowTest1 { public static void main(String[] args) { Window1 w = new Window1(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); t1.start(); t2.start(); //######## 使用同步代碼塊解決繼承Thread類的方式的線程安全問題 ############## class Window2 extends Thread{ private static int ticket = 100; @Override public void run() while(true){ synchronized (Window2.class){//Class clazz = Window2.class,Window2.class只會加載一次 if(ticket > 0){ System.out.println(getName() + ":賣票,票號為:" + ticket); ticket--; }else{ break; public class WindowTest2 { public static void main(String[] args) { Window2 t1 = new Window2(); Window2 t2 = new Window2(); Window2 t3 = new Window2(); t1.start(); t2.start(); t3.start();
方式二:同步方法。
如果操作共享數據的代碼完整的聲明在一個方法中,我們不妨將此方法聲明同步的。
1. 同步方法仍然涉及到同步監視器,只是不需要我們顯式的聲明。
2. 非靜態的同步方法,同步監視器是:this
靜態的同步方法,同步監視器是:當前類本身
//使用同步方法解決實現Runnable接口的線程安全問題 class Window3 implements Runnable { private int ticket = 100; @Override public void run() { while (true) { show(); private synchronized void show(){//同步監視器:this if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket); ticket--; } //使用同步方法處理繼承Thread類的方式中的線程安全問題 class Window4 extends Thread { private static int ticket = 100; @Override public void run() { while (true) { show(); private static synchronized void show(){// staic靜態的,意味著同步監視器:Window4.class if (ticket > 0) { System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket); ticket--;
class Window4 extends Thread { private static int ticket = 100; @Override public void run() { while (true) { show(); } } private static synchronized void show(){//同步監視器:Window4.class //private synchronized void show(){ //同步監視器:t1,t2,t3。此種解決方式是錯誤的 if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":賣票,票號為:" + ticket); ticket--; } } } public class WindowTest4 { public static void main(String[] args) { Window4 t1 = new Window4(); Window4 t2 = new Window4(); Window4 t3 = new Window4(); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
方式三:Lock鎖??--- JDK5.0新增
1. synchronized 與 Lock的異同?
相同:二者都可以解決線程安全問題
不同:synchronized機制在執行完相應的同步代碼以后,自動的釋放同步監視器
Lock需要手動的啟動同步(lock()),同時結束同步也需要手動的實現(unlock())
2.優先使用順序:
Lock? 同步代碼塊(已經進入了方法體,分配了相應資源)
同步方法(在方法體之外)
class Window implements Runnable{ private int ticket = 100; //1.實例化ReentrantLock private ReentrantLock lock = new ReentrantLock(); @Override public void run() { while(true){ try{ //2.調用鎖定方法lock() lock.lock(); if(ticket > 0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":售票,票號為:" + ticket); ticket--; }else{ break; } }finally { //3.調用解鎖方法:unlock() lock.unlock(); } } } } public class LockTest { public static void main(String[] args) { Window w = new Window(); Thread t1 = new Thread(w); Thread t2 = new Thread(w); Thread t3 = new Thread(w); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start(); t2.start(); t3.start(); } }
線程通信:使用兩個線程打印 1-100。線程1, 線程2 交替打印
涉及到的三個方法:
wait():一旦執行此方法,? 當前線程就進入阻塞狀態,并釋放同步監視器。
notify():一旦執行此方法,就會喚醒被wait的一個線程。如果有多個線程被wait,就喚醒優先級高的那個。
notifyAll():一旦執行此方法,就會喚醒所有被wait的線程。
說明:
1.wait(),notify(),notifyAll()三個方法必須使用在同步代碼塊或同步方法中。
2.wait(),notify(),notifyAll()三個方法的調用者必須是同步代碼塊或同步方法中的同步監視器。否則,會出現IllegalMonitorStateException異常
3.wait(),notify(),notifyAll()三個方法是定義在java.lang.Object類中。
面試題:sleep() 和 wait()的異同?
1.相同點:一旦執行方法,都可以使得當前的線程進入阻塞狀態。
2.不同點:1)兩個方法聲明的位置不同:Thread類中聲明sleep() , Object類中聲明wait()
2)調用的要求不同:sleep()可以在任何需要的場景下調用。 wait()必須使用在同步代碼塊或同步方法中
3)關于是否釋放同步監視器:如果兩個方法都使用在同步代碼塊或同步方法中,sleep()不會釋放鎖,wait()會釋放鎖。
class Number implements Runnable{ private int number = 1; private Object obj = new Object(); @Override public void run() { while(true){ synchronized (obj) { obj.notify(); if(number <= 100){ try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":" + number); number++; try { //使得調用如下wait()方法的線程進入阻塞狀態 obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ break; } } } } } public class CommunicationTest { public static void main(String[] args) { Number number = new Number(); Thread t1 = new Thread(number); Thread t2 = new Thread(number); t1.setName("線程1"); t2.setName("線程2"); t1.start(); t2.start(); } }
Java
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。