日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

关于java线程同步的笔记_线程同步(JAVA笔记-线程基础篇)

發布時間:2024/1/23 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 关于java线程同步的笔记_线程同步(JAVA笔记-线程基础篇) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在多線程應用程序中經常會遇到線程同步的問題。比如:兩個線程A、線程B可能會 “同時” 執行同一段代碼,或修改同一個變量。而很多時候我們是不希望這樣的。

這時候,就需要用到線程同步。

多線程引發的問題

為了演示多線程引發問題,我們模仿買票,寫一個簡單的小程序。

實現Runnable模擬買票public class SellTicket implements Runnable {

//有30張票

private int tickets=30;

public void run() {

//寫一個死循環,模擬在不斷的賣票。

while (true){

//票數大于零,代表還有票,繼續賣。

//如果票數小于等于零,也就是沒票了。跳出循環,停止賣票

if(tickets > 0){

sell();

}else{

break;

}

}

}

//買票方法,模擬買票的動作

public void sell(){

//記錄下現在的票數

int oldNumber = tickets;

//賣掉一張后的票數,--tickets代表票數減一,模擬賣掉了一張票

int nowNumber = --tickets;

System.out.println(Thread.currentThread().getName()+",賣出了第("+ oldNumber +")張票,還剩("+ nowNumber +")張票。");

}

}

是線程模擬窗口買票public class MyTest {

public static void main(String[] args) {

//使用同一個免票實例對象

//所以,由于下面所有的窗口都用的是這一個對象。所以他們的票也都是sellTicket的tickets屬性。

SellTicket sellTicket=new SellTicket();

//模擬多個窗口同時買票,每個線程代表一個買票窗口

//Tread()的第二個參數,代表線程名。用線程名,模擬窗口名。

Thread window1=new Thread(sellTicket,"窗口-1");

Thread window2=new Thread(sellTicket,"窗口-2");

Thread window3=new Thread(sellTicket,"窗口-3");

Thread window4=new Thread(sellTicket,"窗口-4");

Thread window5=new Thread(sellTicket,"窗口-5");

//各個窗口開始工作

window1.start();

window2.start();

window3.start();

window4.start();

window5.start();

}

}

> 輸出:

窗口-3,賣出了第(28)張票,還剩(27)張票。

窗口-5,賣出了第(30)張票,還剩(29)張票。

窗口-1,賣出了第(26)張票,還剩(25)張票。

窗口-2,賣出了第(27)張票,還剩(26)張票。

...

...

窗口-2,賣出了第(6)張票,還剩(5)張票。

窗口-5,賣出了第(4)張票,還剩(3)張票。

窗口-3,賣出了第(2)張票,還剩(1)張票。

窗口-4,賣出了第(0)張票,還剩(-1)張票。

窗口-2,賣出了第(1)張票,還剩(0)張票。

上面的例子模擬了多窗口買票,但是看,輸出結果是不是又問題?怎么還有第(-1)張票。難道還有站票不成?當然不存在的,這是我們程序出現了問題。

這就是多線程同時操作統一參數的問題。也就是上面例子中SellTicket對象的private int tickets=30;屬性。

p.s.我運行了好多遍都沒出現錯誤的情況,后面給每個方法休眠了0.5秒才出現上面的錯誤結果。說明多線程不同步代碼發生的錯誤不是百分之百的,只是有一定的概率。

為什么會出現上面這種情況?

眾所周知,多線程的同步不是真的同步執行的。只是CPU切換運行線程所以看上去是幾個線程同步執行。理解了這個概念往下看。(實在不懂的可以百度一下其他大佬的文章,我之后可能還會寫一個筆記來記錄-如果有必要的話)

我們一下最后三個輸出結果,我們發現出錯的是窗口4,也就是第四個線程。...以上的可以省略,當然上面的也可能出現問題。比如兩個不同的窗口賣出了同一張票。但是我沒運行出來這個結果。。。。

窗口-3,賣出了第(2)張票,還剩(1)張票。

窗口-4,賣出了第(0)張票,還剩(-1)張票。

窗口-2,賣出了第(1)張票,還剩(0)張票。

出錯的代碼在下面。//在這里檢測當前票數

if(tickets > 0){

sell();

}else{

break;

}

代碼運行步驟如下:

到最后還剩一張票的時候。這時候 “窗口-4的線程” 運行了。它查看了一下當前票數,發現還有一張。然后進入到if()方法準備運行下面的代碼。

sell();

但是,“窗口-4的線程” 還沒來得及運行sell()方法把票減一,或者運行到sell()里面,還沒來得及減去僅剩的一張票,這時候CPU把運行的權力送給了 “窗口-2的線程” 。此時票數還是1。

“窗口-2的線程” 動作比較快,它很快的檢測當前票數是“1”,并進入if()方法,然后運行sell();將票數減一。

這時候再到 “窗口-4的線程” 此時票數就只剩下0了,但是他還是得運行sell()。也就只能賣出了第(0)張票,還剩(-1)張票了。

Java線程同步

為了避免上面的情況,可以使用以下三個方法來解決。

同步代碼塊

同步方法

同步鎖

同步代碼塊

直接上代碼

synchronized (this){

//這里的代碼,只允許一個線程運行。

//等一個線程運行結束,把鎖交給下一個等待的線程運行。

}

說明

1.this: 這里的this是同步鎖也叫同步監聽對象。

2.this可以是任何對象。

3.但是一般把當前多線程,并發訪問的共同資源當作同步鎖。在例子中也就是sellTicket對象。所以在對象里面可以寫程this。

修改上面的例子

public void run() {

//寫一個死循環,模擬在不斷的賣票。

while (true){

//票數大于零,代表還有票,繼續賣。

//如果票數小于等于零,也就是沒票了。跳出循環,停止賣票

synchronized (this.getClass()){

if(tickets > 0){

sell();

}else{

break;

}

}

}

}

同步方法

同步方法,其實跟同步代碼塊差不多,不過這個是在方法上添加synchronized關鍵字。

public synchronized void sell(){

//這里的代碼,只允許一個線程運行。

//等一個線程運行結束,把鎖交給下一個等待的線程運行。

}

要在上面的例子中使用同步方法,需要改一下。把if()判斷放到sell()方法里面。

public synchronized void sell(){

//在sell()方法在判斷下當前票數

if(tickets > 0){

//記錄下現在的票數

int oldNumber = tickets;

//賣掉一張后的票數,--tickets代表票數減一,模擬賣掉了一張票

int nowNumber = --tickets;

System.out.println(Thread.currentThread().getName()+",賣出了第("+ oldNumber +")張票,還剩("+ nowNumber +")張票。");

}

}

同步鎖

以上兩種方法,都需要一個關鍵字synchronized。同步鎖需要用到一個接口。

public interface Lock {

//生源n多代碼,詳細的去看源碼。

}

接口里面有兩個比較重要的方法。

/**

* 請求一個鎖

* Acquires the lock.

*/

void lock();

/**

* 釋放鎖

* Releases the lock.

*/

void unlock();

在這兩個方法之間的代碼都是同步的。

但是Lock只是個接口,沒法使用。

JUC包給我們一個常用的實現類ReentrantLock。部分源碼如下:

public class ReentrantLock implements Lock, java.io.Serializable {

//感興趣的可以去看看源碼

}

修改我們的案例代碼

public class SellTicket implements Runnable {

//有30張票

private int tickets=30;

Lock lock=new ReentrantLock();

public void run() {

//寫一個死循環,模擬在不斷的賣票。

while (true){

//票數大于零,代表還有票,繼續賣。

//如果票數小于等于零,也就是沒票了。跳出循環,停止賣票

lock.lock();

//synchronized (this.getClass()){

if(tickets > 0){

sell();

}else{

break;

}

//}

lock.unlock();

}

}

//買票方法,模擬買票的動作

public synchronized void sell(){

//記錄下現在的票數

int oldNumber = tickets;

//賣掉一張后的票數,--tickets代表票數減一,模擬賣掉了一張票

int nowNumber = --tickets;

System.out.println(Thread.currentThread().getName()+",賣出了第("+ oldNumber +")張票,還剩("+ nowNumber +")張票。");

}

}

瞎總結

需要同步就要加上鎖。代碼鎖了,其他線程就只能在門外等著。

無論是同步塊、同步方法、鎖機制。都有一個范圍。前兩個是在大括號中間{},后者在兩個方法中間lock();和unlock();

但是加上同步的時候,里面的代碼就只能由一個線程一次執行完。這樣就違反我們使用多線程的目的。(俗稱效率低下)

所以為了提高效率,我們要盡量想辦法,把加鎖的代碼范圍縮小,縮小,再縮小。(前提是程序不會因為多線程出問題,畢竟安全比效率重要)

標簽:tickets,同步,JAVA,張票,筆記,線程,窗口,票數

來源: https://www.cnblogs.com/Eastry/p/13081157.html

總結

以上是生活随笔為你收集整理的关于java线程同步的笔记_线程同步(JAVA笔记-线程基础篇)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。