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