日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

【Java线程安全】 synchronized同步方法、同步块:模拟抢票、模拟取款

發布時間:2024/2/28 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Java线程安全】 synchronized同步方法、同步块:模拟抢票、模拟取款 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

synchronized的使用

大佬之所以叫大佬,就是因為他們即使一次看不懂,看二十遍也要看懂,再對萌新說:這個方法不是挺簡單的嘛


1、同步方法

要注意的是,synchronized鎖的不是方法,而是對象的資源。

33行,成員方法鎖的是this,也就是p,方法中使用的資源必須都是對象p的資源,才能正確的鎖住。

package cn.hanquan.test;public class Test {public static void main(String[] args) throws InterruptedException {Person p = new Person();new Thread(p, "程序猿").start();new Thread(p, "傻子").start();new Thread(p, "世紀帥男").start();} }class Person implements Runnable {private int ticket = 100;@Overridepublic void run() {while (ticket > 0) {try {Thread.sleep(100);grab();} catch (InterruptedException e) {e.printStackTrace();}}}// 同步方法:搶票private synchronized void grab() throws InterruptedException {if (ticket <= 0) {return;}ticket--;System.out.println(Thread.currentThread().getName() + "\t" + ticket);} }

在以上代碼中,

// 同步方法:搶票private synchronized void grab() throws InterruptedException {if (ticket <= 0) {return;}ticket--;System.out.println(Thread.currentThread().getName() + "\t" + ticket);}

等同于:

// 同步方法:搶票private void grab() throws InterruptedException {synchronized (this) {if (ticket <= 0) {return;}ticket--;System.out.println(Thread.currentThread().getName() + "\t" + ticket);}}

說明:synchronized對方法的同步,其實鎖的就是this對象。

而下面這種方式不可以,因為ticket的對象是變動的:

// 同步方法:搶票private void grab() throws InterruptedException {synchronized ((Integer) ticket) {// 這里要的是引用類型,ticket是int類型,需要強轉一下。if (ticket <= 0) {return;}ticket--;System.out.println(Thread.currentThread().getName() + "\t" + ticket);}}

鎖this的時候,雖然this的屬性在變,但是this這個大的對象不變。但是 ticket 這個數是一直在變的,把它轉化成對象(Integer) ticket之后,對象一直在變。這樣不可以。synchronized用于鎖不變的對象才可以,變化的對象鎖不住。

  • 這里的變指的不是內容,而是地址

性能的再優化:double checking

看似代碼變多了,實際上減少了鎖定的范圍(在鎖定之前過濾一下,如果票數小于0,就不鎖了,直接返回)


2、同步塊

(1)線程不安全示例

package cn.hanquan.test;/** 線程安全----使用同步塊*/public class Account {public int money = 100;// 賬戶余額public static void main(String[] args) throws InterruptedException {// 賬戶Account account = new Account();ATM you = new ATM(account, 80, "你");ATM wife = new ATM(account, 90, "她");you.start();wife.start();} }// 模擬取款機 class ATM extends Thread {Account account;int drawMoney;int packetTotal;// constructorpublic ATM(Account account, int drawMoney, String name) {super(name);this.account = account;this.drawMoney = drawMoney;}@Overridepublic void run() {try {test();} catch (InterruptedException e) {e.printStackTrace();}}// 取錢操作public synchronized void test() throws InterruptedException {// 這里的account是作為參數傳進來的if (account.money - drawMoney < 0) {return;}Thread.sleep(1000);account.money -= drawMoney;packetTotal += drawMoney;System.out.println("【" + this.getName() + "】 " + "賬戶余額:" + account.money + " 口袋的錢:" + packetTotal);} }

運行結果:

【她】 賬戶余額:-70 口袋的錢:90
【你】 賬戶余額:-70 口袋的錢:80

看運行結果,發現:賬戶余額成了負數。

也就是說,當余額不足以取出時,仍然進行了取款操作。

這樣的結果說明,synchronized沒有鎖住正確的對象。我們發現,實際上,public synchronized void test()鎖住的,是this所指向的ATM本身的資源;而取款操作,改變的是外部的account對象的資源。

也就是說,應該鎖住的是銀行賬戶,而不是ATM取款機本身。

因此,做出以下改進:

(2)線程安全示例

下面是上例中代碼的改進,實現了線程安全。注意44行使用的同步塊。

package cn.hanquan.test;/** 線程安全----使用同步塊*/public class Account {public int money = 100;// 賬戶余額public static void main(String[] args) throws InterruptedException {// 賬戶Account account = new Account();ATM you = new ATM(account, 80, "你");ATM wife = new ATM(account, 90, "她");you.start();wife.start();} }// 模擬取款機 class ATM extends Thread {Account account;int drawMoney;int packetTotal;// constructorpublic ATM(Account account, int drawMoney, String name) {super(name);this.account = account;this.drawMoney = drawMoney;}@Overridepublic void run() {try {test();} catch (InterruptedException e) {e.printStackTrace();}}// 取錢操作public void test() throws InterruptedException {synchronized (account) {// account本身是獨立的類,作為參數才能傳進來的if (account.money - drawMoney < 0) {return;}Thread.sleep(1000);account.money -= drawMoney;packetTotal += drawMoney;System.out.println("【" + this.getName() + "】 " + "賬戶余額:" + account.money + " 口袋的錢:" + packetTotal);}} }

運行結果:

【你】 賬戶余額:20 口袋的錢:80


3、synchronized在容器中的使用

22行前面要加一個sleep(100),因為要避免還沒數完數就打印了的情況。

總結

以上是生活随笔為你收集整理的【Java线程安全】 synchronized同步方法、同步块:模拟抢票、模拟取款的全部內容,希望文章能夠幫你解決所遇到的問題。

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