線程鎖的使用
本文內(nèi)容
何時該使用線程鎖.
線程鎖的寫法.
以線程鎖的例子來理解線程的調(diào)度。
使用線程鎖的場合
程序中經(jīng)常采用多線程處理,這可以充分利用系統(tǒng)資源,縮短程序響應(yīng)時間,改善用戶體驗(yàn);如果程序中只使用單線程,那么程序的速度和響應(yīng)無疑會大打折扣。
但是,程序采用了多線程后,你就必須認(rèn)真考慮線程調(diào)度的問題,如果調(diào)度不當(dāng),要么造成程序出錯,要么造成荒謬的結(jié)果。
一個諷刺僵化體制的笑話
前蘇聯(lián)某官員去視察植樹造林的情況,現(xiàn)場他看到一個人在遠(yuǎn)處挖坑,其后不遠(yuǎn)另一個人在把剛挖出的坑逐個填上,官員很費(fèi)解于是詢問陪同人員,當(dāng)?shù)毓芾砣藛T說“負(fù)責(zé)種樹的人今天病了”。
上面這個笑話如果發(fā)生在程序中就是線程調(diào)度的問題,種樹這個任務(wù)有三個線程:挖坑線程,種樹線程和填坑線程,后面的線程必須等前一個線程完成才能進(jìn)行,而不是按時間順序來進(jìn)行,否則一旦一個線程出錯就會出現(xiàn)上面荒謬的結(jié)果。
用線程鎖來處理兩個線程先后執(zhí)行的情況
在程序中,和種樹一樣,很多任務(wù)也必須以確定的先后秩序執(zhí)行,對于兩個線程必須以先后秩序執(zhí)行的情況,我們可以用線程鎖來處理。
線程鎖的大致思想是:如果線程A和線程B會執(zhí)行實(shí)例的兩個函數(shù)a和b,如果A必須在B之前運(yùn)行,那么可以在B進(jìn)入b函數(shù)時讓B進(jìn)入wait set,直到A執(zhí)行完a函數(shù)再把B從wait set中激活。這樣就保證了B必定在A之后運(yùn)行,無論在之前它們的時間先后順序是怎樣的。
線程鎖的代碼
如右,SwingComponentLock的實(shí)例就是一個線程鎖,lock函數(shù)用于鎖定線程,當(dāng)完成狀態(tài)isCompleted為false時進(jìn)入的線程會進(jìn)入SwingComponentLock的實(shí)例的wait set,已完成則不會;要激活SwingComponentLock的實(shí)例的wait set中等待的線程需要執(zhí)行unlock函數(shù)。
public class SwingComponentLock {
// 是否初始化完畢
boolean isCompleted = false;
/**
* 鎖定線程
*/
public synchronized void lock() {
while (!isCompleted) {
try {
wait();
} catch (Exception e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}
}
/**
* 解鎖線程
*
*/
public synchronized void unlock() {
isCompleted = true;
notifyAll();
}
}
線程鎖的使用
public class TreeViewPanel extends BasePanel {
// 表空間和表樹
private JTree tree;
// 這個是防樹還未初始化好就被刷新用的
private SwingComponentLock treeLock;
protected void setupComponents() {
// 初始化鎖
treeLock = new SwingComponentLock();
// 創(chuàng)建根節(jié)點(diǎn)
DefaultMutableTreeNode root = new DefaultMutableTreeNode("DB");
tree = new JTree(root);
// 設(shè)置布局并裝入樹
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JScrollPane(tree));
// 設(shè)置樹節(jié)點(diǎn)的圖標(biāo)
setupTreeNodeIcons();
// 解除對樹的鎖定
treeLock.unlock();
}
/**
* 刷新樹視圖
*
* @param schemas
*/
public synchronized void refreshTree(List<SchemaTable> schemas) {
treeLock.lock();
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
root.removeAllChildren();
for (SchemaTable schemaTable : schemas) {
DefaultMutableTreeNode schemaNode = new DefaultMutableTreeNode(
schemaTable.getSchema());
for (String table : schemaTable.getTables()) {
schemaNode.add(new DefaultMutableTreeNode(table));
}
root.add(schemaNode);
}
model.reload();
}
講解
上頁中,setupComponents函數(shù)是Swing主線程執(zhí)行的,而refreshTree函數(shù)是另外的線程執(zhí)行(初始化時程序開辟一個線程執(zhí)行,其后執(zhí)行由用戶操作決定)。 refreshTree函數(shù)必須要等setupComponents函數(shù)把tree初始化完畢后才能執(zhí)行,而tree初始化的時間較長,可能在初始化的過程中執(zhí)行refreshTree的線程就進(jìn)入了,這就會造成問題。
程序使用了一個SwingComponentLock來解決這個問題,setupComponents一開始就創(chuàng)建SwingComponentLock的實(shí)例treeLock,然后執(zhí)行refreshTree的線程以來就會進(jìn)入treeLock的wait set,變成等待狀態(tài),不會往下執(zhí)行,這是不管tree是否初始化完畢都不會出錯;而setupComponents執(zhí)行到底部會激活treeLock的wait set中等待的線程,這時再執(zhí)行refreshTree剩下的代碼就不會有任何問題,因?yàn)閟etupComponents執(zhí)行完畢tree已經(jīng)初始化好了。
讓線程等待和激活線程的代碼都在SwingComponentLock類中,這樣的封裝對復(fù)用很有好處,如果其它復(fù)雜組件如table也要依此辦理直接創(chuàng)建SwingComponentLock類的實(shí)例就可以了。如果把wait和notifyAll寫在TreeViewPanel類中就不會這樣方便了。
總結(jié)
線程鎖用于必須以固定順序執(zhí)行的多個線程的調(diào)度。
線程鎖的思想是先鎖定后序線程,然后讓線序線程完成任務(wù)再接觸對后序線程的鎖定。
線程鎖的寫法和使用一定要理解記憶下來。
posted on 2008-02-22 14:20 和風(fēng)細(xì)雨 閱讀(4549) 評論(0) 編輯 收藏 所屬分類: 線程