(Java多线程)线程安全问题
文章目錄
- 線程安全
- 定義
- 售票模擬案例
- 注意
- 線程同步
- 同步代碼塊
- 同步方法
- Lock鎖
線程安全
定義
- 如果有多個線程在同時運行,而這些線程可能會同時運行這段代碼。程序每次運行結果和單線程運行的結果是一樣的,而且其他變量的值也和預期的是一樣的,就是線程安全的。
售票模擬案例
模擬票
public class Ticket implements Runnable{//設置初始票量100private int ticket=100;/*賣票*/@Overridepublic void run() {while (true){if (ticket>0) {//有余票try {Thread.sleep(10);//模擬出票時間} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在賣:" + ticket );ticket--;}else {break;}}} }模擬售票窗口
public class Demo04Ticket {public static void main(String[] args) {//創建線程任務對象Ticket ticket = new Ticket();//創建三個線程Thread t1 = new Thread(ticket,"窗口1");Thread t2 = new Thread(ticket,"窗口2");Thread t3 = new Thread(ticket,"窗口3");//同時賣票t1.start();t2.start();t3.start();} }結果:
發現程序出現了問題:
幾個(窗口)線程的票數不同步了,這種問題就稱為線程不安全。
注意
線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步 ,否則的話就可能影響線程安全。
線程同步
- 當我們使用多個線程訪問同-資源的時候,且多個線程中對資源有寫的操作,就容易出現線程安全問題。
- 要解決上述多線程并發訪問一一個資源的安全性問題:也就是解決重復票與不存在票問題,Java中提供了同步機制(synchronized)來解決。
窗口1線程進入操作的時候,窗口2和窗口3線程只能在外等著,窗口1操作結束,窗口1和窗口2和窗口3才有機會進入代碼去執行。
也就是說在某個線程修改共享資源的時候,其他線程不能修改該資源,等待修改完畢同步之后,才能去搶奪CPU資源,完成對應的操作,保證了數據的同步性,解決了線程不安全的現象。
完成同步操作的三個方法:
同步代碼塊
- 同步代碼塊: synchronized關鍵字可以用于方法的某個區塊中,表示只對這個區塊的資源實行互斥訪問。
格式:
synchronized(同步鎖){需要同步操作的代碼 }同步鎖:
注意:在任何時候,最多允許一個線程擁有同步鎖,誰拿到鎖誰就能進入代碼塊,其他線程只能在外面等待(BLOCKED)。
使用同步代碼塊解決上述代碼:
public class Ticket implements Runnable{//設置初始票量100private int ticket=100;//創建鎖對象Object lock = new Object();/*賣票*/@Overridepublic void run() {while (true) {//同步代碼塊synchronized (lock) {if (ticket > 0) {//有余票try {Thread.sleep(100);//模擬出票時間} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在賣" + ticket+"號票");ticket--;} else {break;}}}} }
使用同步代碼快之后,線程安全問題就解決了。
同步方法
- 同步方法:使用synchronized修飾的方法,就叫做同步方法,保證A線程執行該方法的時候,其他線程只能在方法外等待。
格式:
public synchronized void method(){可能會產生線程安全問題的代碼 }對于非static方法,同步鎖就是this.
對于static方法,我們使用當前方法所在類的字節碼對象(類名.class)。
上述代碼使用同步方法示例:
public class Ticket implements Runnable{//設置初始票量100private int ticket=100;/*賣票*/@Overridepublic void run() {while (true) {payTicket();}}//同步方法private synchronized void payTicket() {if (ticket > 0) {//有余票try {Thread.sleep(100);//模擬出票時間} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在賣" + ticket+"號票");ticket--;}} }Lock鎖
java.util.locks.lock機制提供了比synchronized代碼塊和synchronized方法更廣泛的鎖定操作,同步代碼快和同步方法具有的功能Lock都有,除此之外更強大,更體現面向對象。
Lock鎖也稱同步鎖,加鎖與釋放鎖方法化了,如下:
- public void lock:加同步鎖
- public void unlock:釋放同步鎖
使用方法如下:
public class Ticket implements Runnable{//設置初始票量100private int ticket=100;//創建鎖對象Lock lock = new ReentrantLock();/*賣票*/@Overridepublic void run() {while (true){//獲取鎖lock.lock();if (ticket>0) {//有余票try {Thread.sleep(10);//模擬出票時間System.out.println(Thread.currentThread().getName() + "正在賣:" + ticket );ticket--;} catch (InterruptedException e) {e.printStackTrace();}finally {lock.unlock();//無論是否執行成功,都釋放鎖}}else {break;}}} }總結
以上是生活随笔為你收集整理的(Java多线程)线程安全问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (仿头条APP项目)8.新闻详情页面实现
- 下一篇: (Java多线程)线程状态