Java 線程第三版 第五章 極簡同步技巧 讀書筆記
阿新 • • 發佈:2017-05-06
prev ear ont java else 停止 第三版 不同的 結合
取得由競爭的鎖須要在虛擬機的層面上執行很多其它的程序代碼。
要取得有競爭鎖的線程總是必須等到鎖被釋放後。
atomic package支持更復雜的變量類型嗎?
變量替換
score與char2type變量已經改成atomic變量。
以上resetScroe方法中對兩個變量進行改動。盡管使用atomic能保證每一個變量的原子性,可是假設多個線程同一時候運行resetScore方法依舊會出現競態條件。
有可能一個線程運行resetScore的第一行代碼score.set(0);還未運行第二行,而另外一個線程可能已經運行newCharacter方法獲取char2type的值,可是之前的線程運行resetScore方法還未對char2type進行改動。由於resetScore整個方法並非原子的。
變更算法
resetGenerator與resetTypist兩個方法,以resetGenerator方法為例,將generator變量變成AtomicRerence相同引發上面描寫敘述的問題。
一、能避免同步嗎?
取得鎖會由於下面原因導致成本非常高:取得由競爭的鎖須要在虛擬機的層面上執行很多其它的程序代碼。
要取得有競爭鎖的線程總是必須等到鎖被釋放後。
1. 寄存器的效應
計算機有一定數量的主寄存器用來存儲與程序有關的數據。
從邏輯上的觀點來看,每一個Thread都有自己的一組寄存器。當操作系統將某個Thread分配給CPU時,它會把該Thread特有的信息載入到CPU的寄存器中。
在分配不同的Thread給CPU之前,它會將寄存器的信息存下來。
所以Thread間絕不會共享保存在寄存器的數據。
當虛擬機進入synchronized方法或者塊時。它必須又一次載入本來已經緩存到自有寄存器上的數據。在虛擬機離開synchroized方法或者塊之前,它必須把自有寄存器存入主寄存器中。
2. 重排語句的效應
在單獨線程中。程序總是依照代碼一行行的運行的。可是假設是多個線程並發,Java並不保證每一個線程run方法的運行順序,也就是可能當中一個線程運行到一半就會被臨時停止。運行其它線程,之後再切換回來。
多個線程間調度運行的無序性就是重排語句的效應。
3. 雙重檢查的Locking
二、Atomic變量
1. Atomic Class的概述
AtomicInteger, AtomicLong, AtomicBoolean, AtomicRefrences。Atomic的功能實現時通過使用use-level的Java程序無法訪問的固有方法來完畢的。atomic package支持更復雜的變量類型嗎?
一些不支持。比如字符或者浮點。
AtomicStampedReference可以讓mark或stamp跟在不論什麽對象的引用上。
AtomicMarkableReference提供一個包括對象引用結合boolean的數據結構。
2. 使用Atomic Class
import javax.swing.*; import java.awt.event.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import javathreads.examples.ch05.*; public class ScoreLabel extends JLabel implements CharacterListener { private AtomicInteger score = new AtomicInteger(0); private AtomicInteger char2type = new AtomicInteger(-1); private AtomicReference<CharacterSource> generator = null; private AtomicReference<CharacterSource> typist = null; public ScoreLabel (CharacterSource generator, CharacterSource typist) { this.generator = new AtomicReference(generator); this.typist = new AtomicReference(typist); if (generator != null) generator.addCharacterListener(this); if (typist != null) typist.addCharacterListener(this); } public ScoreLabel () { this(null, null); } public void resetGenerator(CharacterSource newGenerator) { CharacterSource oldGenerator; if (newGenerator != null) newGenerator.addCharacterListener(this); oldGenerator = generator.getAndSet(newGenerator); if (oldGenerator != null) oldGenerator.removeCharacterListener(this); } public void resetTypist(CharacterSource newTypist) { CharacterSource oldTypist; if (newTypist != null) newTypist.addCharacterListener(this); oldTypist = typist.getAndSet(newTypist); if (oldTypist != null) oldTypist.removeCharacterListener(this); } public void resetScore() { score.set(0); char2type.set(-1); setScore(); } private void setScore() { // This method will be explained later in chapter 7 SwingUtilities.invokeLater(new Runnable() { public void run() { setText(Integer.toString(score.get())); } }); } public void newCharacter(CharacterEvent ce) { int oldChar2type; // Previous character not typed correctly - 1 point penalty if (ce.source == generator.get()) { oldChar2type = char2type.getAndSet(ce.character); if (oldChar2type != -1) { score.decrementAndGet(); setScore(); } } // If character is extraneous - 1 point penalty // If character does not match - 1 point penalty else if (ce.source == typist.get()) { while (true) { oldChar2type = char2type.get(); if (oldChar2type != ce.character) { score.decrementAndGet(); break; } else if (char2type.compareAndSet(oldChar2type, -1)) { score.incrementAndGet(); break; } } setScore(); } } }
變量替換
score與char2type變量已經改成atomic變量。
以上resetScroe方法中對兩個變量進行改動。盡管使用atomic能保證每一個變量的原子性,可是假設多個線程同一時候運行resetScore方法依舊會出現競態條件。
有可能一個線程運行resetScore的第一行代碼score.set(0);還未運行第二行,而另外一個線程可能已經運行newCharacter方法獲取char2type的值,可是之前的線程運行resetScore方法還未對char2type進行改動。由於resetScore整個方法並非原子的。
變更算法
resetGenerator與resetTypist兩個方法,以resetGenerator方法為例,將generator變量變成AtomicRerence相同引發上面描寫敘述的問題。
通知與Atomic變量
import java.awt.*; import javax.swing.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import javathreads.examples.ch05.*; public class AnimatedCharacterDisplayCanvas extends CharacterDisplayCanvas implements CharacterListener, Runnable { private AtomicBoolean done = new AtomicBoolean(true); private AtomicInteger curX = new AtomicInteger(0); private AtomicInteger tempChar = new AtomicInteger(0); private Thread timer = null; public AnimatedCharacterDisplayCanvas() { startAnimationThread(); } public AnimatedCharacterDisplayCanvas(CharacterSource cs) { super(cs); startAnimationThread(); } private void startAnimationThread() { if (timer == null) { timer = new Thread(this); timer.start(); } } public void newCharacter(CharacterEvent ce) { curX.set(0); tempChar.set(ce.character); repaint(); } protected void paintComponent(Graphics gc) { char[] localTmpChar = new char[1]; localTmpChar[0] = (char) tempChar.get(); int localCurX = curX.get(); Dimension d = getSize(); int charWidth = fm.charWidth(localTmpChar[0]); gc.clearRect(0, 0, d.width, d.height); if (localTmpChar[0] == 0)
Java 線程第三版 第五章 極簡同步技巧 讀書筆記