201521123087 《Java程序設計》第11周學習總結
1. 本周學習總結
2. 書面作業
本次PTA作業題集多線程
-
互斥訪問與同步訪問
完成題集4-4(互斥訪問)與4-5(同步訪問)
1.1 除了使用synchronized修飾方法實現互斥同步訪問,還有什麽辦法實現互斥同步訪問(請出現相關代碼)? 答:使用Lock方法和Condition對象:
import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Account { private int balance; private Lock lock = new ReentrantLock(); private Condition plus = lock.newCondition(); public Account(int balance) { super(); this.balance = balance; } public int getBalance() {// synchronized return balance; } public void deposit(int money) {// synchronized lock.lock(); try { this.balance=getBalance()+money; plus.signalAll(); } finally { lock.unlock(); } } public void withdraw(int money) {// synchronized lock.lock(); try { while (getBalance() <= 0) { try{ plus.await(); }catch(InterruptedException e){ e.printStackTrace(); } } this.balance=getBalance() - money; plus.signalAll(); } finally { lock.unlock(); } } }
1.2 同步代碼塊與同步方法有何區別? 答:同步方法直接在方法前加synchronized關鍵字,同步代碼塊則在方法內部添加鎖,同步塊需要註明鎖定對象,而同步方法默認鎖定this,同步的方法一次只能有一個線程進入,其他線程等待。在考慮性能方面,最好使用同步塊來減少鎖定範圍提高並發效率。
1.3 實現互斥訪問的原理是什麽?請使用對象鎖概念並結合相應的代碼塊進行說明。當程序執行synchronized同步代碼塊或者同步方法時,線程的狀態是怎麽變化的? 答當資源被訪問,上鎖,但在同一時刻,只能有一個線程獲得某個資源的鎖,訪問該資源,其他線程無法訪問。當停止訪問時,解鎖,讓其他線程訪問。
class Counter { private static int id = 0; public synchronized static void addId() { id++; } public static int getId() { return id; }
假設現在有兩個線程t1,t2,同時執行該方法,當程序執行synchronized標記的同步方法時,操作步驟如下: 1.線程t1獲得id的鎖,讀取id的值,為0;(線程t2等待) 2.線程t1在取到的值上加1,結果為1; 3.線程t1將結果id=1存回id,id的鎖被解鎖資源釋放; 4.線程t2獲得id上的鎖,讀取id的值,為1; 5.線程t2在取到的值上加1,結果為2; 6.線程t1將結果id=2存回id,id的鎖被解鎖資源釋放;id結果為2。
1.4 Java多線程中使用什麽關鍵字實現線程之間的通信,進而實現線程的協同工作?為什麽同步訪問一般都要放到synchronized方法或者代碼塊中? 答:Java多線程中用wait()和notify()/notifyAll()方法來實現線程之間的通信,或者使用Condition對象。同步訪問放到synchronized方法或者代碼塊中是為了防止出現多個線程訪問同一資源所引起的沖突。
-
交替執行
要實現交替訪問,首先要將方法全部都用synchronized來修飾,確保所有變量都持有鎖。其次線程之間的協作可以使用wait()和notify()並配合上boolean變量來完成,要做到交替執行,需要中間橋梁來確認是否執行該任務,這個橋梁就是flag。 -
互斥訪問
3.1 修改TestUnSynchronizedThread.java源代碼使其可以同步訪問。(關鍵代碼截圖,需出現學號)
3.2 進一步使用執行器改進相應代碼(關鍵代碼截圖,需出現學號)
參考資料:Java多線程之Executor、ExecutorService、Executors、Callable、Future與FutureTask -
線程間的合作:生產者消費者問題
4.1 運行MyProducerConsumerTest.java。正常運行結果應該是倉庫還剩0個貨物。多運行幾次,觀察結果,並回答:結果正常嗎?哪裏不正常?為什麽? 答:結果不正常,剩余個數從0到10皆有,因為生產者與消費者的存取速度不一致,所以會導致生產和消費不一致。也就是說沒有實現線程之間通信的關鍵字,故線程之間沒有合作,就會產生錯亂。
4.2 使用synchronized, wait, notify
解決該問題(關鍵代碼截圖,需出現學號) -
查詢資料回答:什麽是線程安全?(用自己的話與代碼總結,寫自己看的懂的作業) 答:線程安全就是說多線程訪問同一代碼,不會產生不確定的結果。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變量的值也和預期的是一樣的,就是線程安全的,也就是線程同步。
例如
public synchronized static void addId() { id++; } public synchronized static void subtractId() { id--; }
多個線程同時運行以上代碼,synchronized關鍵字使得線程同步,多個線程運行結果和單個線程運行結果一致,都為0,
3. 碼雲上代碼提交記錄
3.1. 碼雲代碼提交記錄
-
3.2 截圖多線程PTA提交列表
201521123087 《Java程序設計》第11周學習總結