對一種特殊的資源——對象中的內存——Java提供了內建的機制來防止它們的沖突
synchronized void f() { /*
synchronized void g() { /*
每個對象都包含了一把鎖(也叫作
每個類也有自己的一把鎖(作為類的Class對象的一部分)
注意如果想保護其他某些資源不被多個線程同時訪問
裝備了這個新關鍵字後
//: Sharing
// Using the synchronized keyword to prevent
// multiple access to a particular resource
import java
import java
import java
class TwoCounter
private boolean started = false;
private TextField
t
t
private Label l =
new Label(
private int count
public TwoCounter
Panel p = new Panel();
p
p
p
c
}
public void start() {
if(!started) {
started = true;
super
}
}
public synchronized void run() {
while (true) {
t
t
try {
sleep(
} catch (InterruptedException e){}
}
}
public synchronized void synchTest() {
Sharing
if(count
l
}
}
class Watcher
private Sharing
public Watcher
this
start();
}
public void run() {
while(true) {
for(int i =
p.s[i].synchTest();
try {
sleep(500);
} catch (InterruptedException e){}
}
}
}
public class Sharing2 extends Applet {
TwoCounter2[] s;
private static int accessCount = 0;
private static TextField aCount =
new TextField("0", 10);
public static void incrementAccess() {
accessCount++;
aCount.setText(Integer.toString(accessCount));
}
private Button
start = new Button("Start"),
observer = new Button("Observe");
private boolean isApplet = true;
private int numCounters = 0;
private int numObservers = 0;
public void init() {
if(isApplet) {
numCounters =
Integer.parseInt(getParameter("size"));
numObservers =
Integer.parseInt(
getParameter("observers"));
}
s = new TwoCounter2[numCounters];
for(int i = 0; i < s.length; i++)
s[i] = new TwoCounter2(this);
Panel p = new Panel();
start.addActionListener(new StartL());
p.add(start);
observer.addActionListener(new ObserverL());
p.add(observer);
p.add(new Label("Access Count"));
p.add(aCount);
add(p);
}
class StartL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < s.length; i++)
s[i].start();
}
}
class ObserverL implements ActionListener {
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < numObservers; i++)
new Watcher2(Sharing2.this);
}
}
public static void main(String[] args) {
Sharing2 applet = new Sharing2();
// This isn't an applet, so set the flag and
// produce the parameter values from args:
applet.isApplet = false;
applet.numCounters =
(args.length == 0 ? 5 :
Integer.parseInt(args[0]));
applet.numObservers =
(args.length < 2 ? 5 :
Integer.parseInt(args[1]));
Frame aFrame = new Frame("Sharing2");
aFrame.addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
aFrame.add(applet, BorderLayout.CENTER);
aFrame.setSize(350, applet.numCounters *100);
applet.init();
applet.start();
aFrame.setVisible(true);
}
}
我們注意到無論run()還是synchTest()都是“同步的”。tW.WIngwIT.cOM如果只同步其中的一個方法,那麼另一個就可以自由忽視對象的鎖定,並可無礙地調用。所以必須記住一個重要的規則:對於訪問某個關鍵共享資源的所有方法,都必須把它們設為synchronized,否則就不能正常地工作。
現在又遇到了一個新問題。Watcher2永遠都不能看到正在進行的事情,因為整個run()方法已設為“同步”。而且由於肯定要為每個對象運行run(),所以鎖永遠不能打開,而synchTest()永遠不會得到調用。之所以能看到這一結果,是因為accessCount根本沒有變化。
為解決這個問題,我們能采取的一個辦法是只將run()中的一部分代碼隔離出來。想用這個辦法隔離出來的那部分代碼叫作“關鍵區域”,而且要用不同的方式來使用synchronized關鍵字,以設置一個關鍵區域。Java通過“同步塊”提供對關鍵區域的支持;這一次,我們用synchronized關鍵字指出對象的鎖用於對其中封閉的代碼進行同步。如下所示:
synchronized(syncObject) {
// This code can be accessed by only
// one thread at a time, assuming all
// threads respect syncObject's lock
}
在能進入同步塊之前,必須在synchObject上取得鎖。如果已有其他線程取得了這把鎖,塊便不能進入,必須等候那把鎖被釋放。
可從整個run()中刪除synchronized關鍵字,換成用一個同步塊包圍兩個關鍵行,從而完成對Sharing2例子的修改。但什麼對象應作為鎖來使用呢?那個對象已由synchTest()標記出來了——也就是當前對象(this)!所以修改過的run()方法象下面這個樣子:
public void run() {
while (true) {
synchronized(this) {
t1.setText(Integer.toString(count1++));
t2.setText(Integer.toString(count2++));
}
try {
sleep(500);
} catch (InterruptedException e){}
}
}
這是必須對Sharing2.java作出的唯一修改,我們會看到盡管兩個計數器永遠不會脫離同步(取決於允許Watcher什麼時候檢查它們),但在run()執行期間,仍然向Watcher提供了足夠的訪問權限。
當然,所有同步都取決於程序員是否勤奮:要訪問共享資源的每一部分代碼都必須封裝到一個適當的同步塊裡。
2. 同步的效率
由於要為同樣的數據編寫兩個方法,所以無論如何都不會給人留下效率很高的印象。看來似乎更好的一種做法是將所有方法都設為自動同步,並完全消除synchronized關鍵字(當然,含有synchronized run()的例子顯示出這樣做是很不通的)。但它也揭示出獲取一把鎖並非一種“廉價”方案——為一次方法調用付出的代價(進入和退出方法,
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19487.html