Java多線程Thread線程安全
阿新 • • 發佈:2018-06-18
窗口 exception ren 問題 oid sys 其它 同步機制 發出
1.什麽是線程安全問題?
從某個線程開始訪問到訪問結束的整個過程,如果有一個訪問對象被其他線程修改,那麽對於當前線程而言就發生了線程安全問題;
如果在整個訪問過程中,無一對象被其他線程修改,就是線程安全的。
2.線程安全問題產生的根本原因
首先是多線程環境,即同時存在有多個操作者,單線程環境不存在線程安全問題。在單線程環境下,任何操作包括修改操作都是操作者自己發出的,
操作者發出操作時不僅有明確的目的,而且意識到操作的影響。
多個操作者(線程)必須操作同一個對象,只有多個操作者同時操作一個對象,行為的影響才能立即傳遞到其他操作者。
多個操作者(線程)對同一對象的操作必須包含修改操作,共同讀取不存在線程安全問題,因為對象不被修改,未發生變化,不能產生影響。
綜上可知,線程安全問題產生的根本原因是共享數據存在被並發修改的可能,即一個線程讀取時,允許另一個線程修改
看實例:模擬火車站售票窗口,開啟三個窗口售票,總票數為100張
實例一:
package com.yyx.test; //模擬火車站售票窗口,開啟三個窗口售票,總票數為100張 //存在線程的安全問題 class Window extends Thread { static int ticket = 100; public void run() { while (true) { if (ticket > 0) {try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票號為:" + ticket--); } else { break; } } } }public class TestWindow { public static void main(String[] args) { Window w1 = new Window(); Window w2 = new Window(); Window w3 = new Window(); w1.setName("窗口1"); w2.setName("窗口2"); w3.setName("窗口3"); w1.start(); w2.start(); w3.start(); } }
實例二:
package com.yyx.test; //使用實現Runnable接口的方式,售票 /* * 此程序存在線程的安全問題:打印車票時,會出現重票、錯票 */ class Window1 implements Runnable { int ticket = 100; public void run() { while (true) { if (ticket > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票號為:" + ticket--); } else { break; } } } } public class TestWindow1 { public static void main(String[] args) { Window1 w = new Window1(); 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(); } }
3.線程安全的解決
必須讓一個線程操作共享數據完畢以後,其它線程才有機會參與共享數據的操作
1)線程的同步機制 synchronized
同步代碼塊
package com.yyx.test; /* 同步代碼塊 * synchronized(同步監視器){ * //需要被同步的代碼塊(即為操作共享數據的代碼) * } * 1.共享數據:多個線程共同操作的同一個數據(變量) * 2.同步監視器:由一個類的對象來充當。哪個線程獲取此監視器,誰就執行大括號裏被同步的代碼。俗稱:鎖 * 要求:所有的線程必須共用同一把鎖! * 註:在實現的方式中,考慮同步的話,可以使用this來充當鎖。但是在繼承的方式中,慎用this! */ class Window implements Runnable { int ticket = 50;// 共享數據 public void run() { while (true) { // this表示當前對象,本題中即為w synchronized (this) { if (ticket > 0) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票號為:" + ticket--); } } } } } public class TestWindow { 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(); } }
同步方法
package com.yyx.test; /* * 同步方法 * 將操作共享數據的方法聲明為synchronized。即此方法為同步方法,能夠保證當其中一個線程執行 * 此方法時,其它線程在外等待直至此線程執行完此方法 */ class Window implements Runnable { int ticket = 50;// 共享數據 public void run() { while (true) { show(); } } public synchronized void show() { if (ticket > 0) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "售票,票號為:" + ticket--); } } } public class TestWindow { 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(); } }
Java多線程Thread線程安全