導航:首頁 > 編程語言 > java線程安全的方法

java線程安全的方法

發布時間:2023-05-02 14:58:04

1. 如何創建線程如何保證線程安全

2. Java中如何使方法線程安全

線程不安全的場合很多,比如像操作系統中的用戶界面、列印機等外設、控制台輸出專,都不允許並發屬(設想兩個程序同時要輸出文字到同一個屏幕,那還不亂套了)
在代碼中,每個線程有自己的堆棧但是共享整個堆,所以訪問那些全局的變數,也必須同步,否則會出現臟讀數據。
同步也不是萬能的良葯。不當的鎖定會導致程序死鎖,而且多線程本身就是為了提高性能,但是同步使用多了,程序又實質上退化成了單線程程序,用多線程的意義也就沒了。

3. 如何確保Java線程安全

java中,線抄程安全的解決方法或過程:
1.如果對象是immutable,則是線程安全的,例如:String,可以放心使用。
2. 如果對象是線程安全的,則放心使用。
3.有條件線程安全,對於Vector和Hashtable一般情況下是線程安全的,但是對於某些特殊情況,需要通過額外的synchronized保證線程安全。
4.使用synchronized關鍵字。

4. 如何創建線程如何保證線程安全

互斥同步是最常見的一種並發正確性保障手段。同步是指在多線程並發訪問共享數據時,保證圓差櫻共享數據在同一時刻只被一個線程使用(同一時刻,只有一個線程在操作共享數據)。而互斥是實現同步的一種手段,臨界區、互斥量和信號量都是主要的互斥實現方式。因此,在這4個字裡面,互斥是因,同步是果;互斥是方法,同步是目的。
隨著硬體指令集的發展,出現了基於沖突檢測的樂觀並發策略,通俗地說,就是先進行操作,如果沒有其他線程爭用共享橘叢數據,那操作就成功了;如果共享數據有爭用,產生了沖突,那就再採用其他的補償措施。(最常見的補償錯誤就是不斷地重試,直到成功為止),這種樂觀的並發策略的許多實現都不需要把線程掛起,因此這種同步操作稱為非阻塞同步。
要保證線程安全慶掘,並不是一定就要進行同步,兩者沒有因果關系。同步只是保證共享數據爭用時的正確性的手段,如果一個方法本來就不涉及共享數據,那它自然就無需任何同步操作去保證正確性,因此會有一些代碼天生就是線程安全的。

5. Java容器類的線程安全

Java容器類的關系圖

Collection

├List 介面

│├LinkedList 鏈表

│├ArrayList 順序結構動態氏悄數組類

│└Vector 向量

│└Stack 棧

└Set

Map

├HashTable

├HashMap

└WeakHashMap List介面

線程安全的和線程不安全的

Vector和HashMap是線程安全的

LinkedList ArrayList和HashMap是線程不安全的

由於同步需要花費首困時間 因此線程安全的執行效率要低於線程不安全的

在多線程操作

案例 多線程操作導致List報NoSuchElementException

java util NoSuchElementException

at java util LinkedList remove(LinkedList java: )

at java util LinkedList removeFirst(LinkedList java: )

at re RegexBuiltins getPattern(RegexBuiltins java: )

解決方法

調用Collections的同步List

List<String> items = Collections synchronizedList(new LinkedList<String>());

public void remove() {

if (!items isEmpty()) {

return items remove( );

}

}

設置標志 同步

LinkedList<String> items = new LinkedList<殲芹渣String>();

String flag= abcdef ;

public void remove() {

synchronized(flag){

if (!items isEmpty()) {

return items removeFirst();

}

}

lishixin/Article/program/Java/hx/201311/27139

6. Java的List如何實現線程安全

解決這個問題通常有兩種方法(個人認為)
一:使用synchronized關鍵字,這個大家應該都很熟悉了,不解釋了;
二:使用Collections.synchronizedList();使用方法如下:
假如你創建的代碼如下:List<Map<String,Object>> data=new ArrayList<Map<String,Object>>();
那麼為了解決這個線程安全問題你可以這么使用Collections.synchronizedList(),如:
List<Map<String,Object>> data=Collections.synchronizedList(new ArrayList<Map<String,Object>>());
其他的都沒變,使用的方法也幾乎與ArrayList一樣,大家可以參考下api文檔;
額外說下 ArrayList與LinkedList;這兩個都是介面List下的一個實現,用法都一樣,但用的場所的有點不同,ArrayList適合於進行大量的隨機訪問的情況下使用,LinkedList適合在表中進行插入、刪除時使用,二者都是非線程安全,解決方法同上(為了避免線程安全,以上採取的方法,特別是第二種,其實是非常損耗性能的)。

7. Java的List如何實現線程安全

Java的List如何實現線程安全?

Collections.synchronizedList(names);效率最高,線程安全

Java的List是我們平時很常用的集合,線程安全對於高並發的場景也十分的重要,那麼List如何才能實現線程安全呢 ?


加鎖

首先大家會想到用Vector,這里我們就不討論了,首先討論的是加鎖,例如下面的代碼


public class Synchronized{

private List<String> names = new LinkedList<>();

public synchronized void addName(String name ){
names.add("abc");
}
public String getName(Integer index){
Lock lock =new ReentrantLock();
lock.lock();
try {
return names.get(index);
}catch (Exception e){
e.printStackTrace();
}
finally {
lock.unlock();
}
return null;
}
}

synchronized一加,或者使用lock 可以實現線程安全,但是這樣的List要是很多個,代碼量會大大增加。

java自帶類

在java中我找到自帶有兩種方法


CopyOnWriteArrayList

CopyOnWrite 寫入時復制,它使一個List同步的替代品,通常情況下提供了更好的並發性,並且避免了再迭代時候對容器的加鎖和復制。通常更適合用於迭代,在多插入的情況下由於多次的復制性能會一定的下降。


下面是add方法的源代碼


public boolean add(E e) {
final ReentrantLock lock = this.lock; // 加鎖 只允許獲得鎖的線程訪問
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
// 創建個長度加1的數組並復制過去
Object[] newElements = Arrays.Of(elements, len + 1);
newElements[len] = e; // 賦值
setArray(newElements); // 設置內部的數組
return true;
} finally {
lock.unlock();
}
}


Collections.synchronizedList

Collections中有許多這個系列的方法例如


主要是利用了裝飾者模式對傳入的集合進行調用 Collotions中有內部類SynchronizedList


static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;

final List<E> list;

SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public E set(int index, E element) {
synchronized (mutex) {return list.set(index, element);}
}
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
public E remove(int index) {
synchronized (mutex) {return list.remove(index);}
}

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;

final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize


這里上面的mutex就是鎖的對象 在構建時候可以指定鎖的對象 主要使用synchronize關鍵字實現線程安全

/**
* @serial include
*/
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;

final List<E> list;

SynchronizedList(List<E> list) {
super(list);
this.list = list;
}
SynchronizedList(List<E> list, Object mutex) {
super(list, mutex);
this.list = list;
}
這里只是列舉SynchronizedList ,其他類類似,可以看下源碼了解下。

測試
public class Main {
public static void main(String[] args) {
List<String> names = new LinkedList<>();
names.add("sub");
names.add("jobs");
// 同步方法1 內部使用lock
long a = System.currentTimeMillis();
List<String> strings = new CopyOnWriteArrayList<>(names);
for (int i = 0; i < 100000; i++) {
strings.add("param1");
}
long b = System.currentTimeMillis();
// 同步方法2 裝飾器模式使用 synchronized
List<String> synchronizedList = Collections.synchronizedList(names);
for (int i = 0; i < 100000; i++) {
synchronizedList.add("param2");
}
long c = System.currentTimeMillis();
System.out.println("CopyOnWriteArrayList time == "+(b-a));
System.out.println("Collections.synchronizedList time == "+(c-b));
}
}


兩者內部使用的方法都不一樣,CopyOnWriteArrayList內部是使用lock進行加鎖解鎖完成單線程訪問,synchronizedList使用的是synchronize

進行了100000次添加後時間對比如下:

可以看出來還是使用了synchronize的集合工具類在添加方面更加快一些,其他方法這里篇幅關系就不測試了,大家有興趣去試一下。

8. java 程序中怎麼保證多線程的運行安全

並發編程三要素(線程的安全性問題體現在):

原子性:原子,即一個不可再被分割的顆粒。原子性指的是一個或多個操作要麼 全部執行成功要麼全部執行失敗。

可見性:一個線程對共享變數的修改,另一個線程能夠立刻看到。 (synchronized,volatile)

有序性:程序執行的順序按照代碼的先後順序執行。(處理器可能會對指令進行 重排序)

出現線程安全問題的原因:

9. Java中如何保證線程安全性

並發(concurrency)一個並不陌生的詞,簡單來說,就是cpu在同一時刻執行多個任務。

而Java並發則由多線程實現的。

在jvm的世界裡,線程就像不相乾的平行空間,串列在虛擬機中。(當然這是比較籠統的說法,線程之間是可以交互的,他們也不一定是串列。)

多線程的存在就是壓榨cpu,提高程序性能,還能減少一定的設計復雜度(用現實的時間思維設計程序)。

這么說來似乎線程就是傳說中的銀彈了,可事實告訴我們真正的銀彈並不存在。

多線程會引出很多難以避免的問題, 如死鎖,臟數據,線程管理的額外開銷,等等。更大大增加了程序設計的復雜度。

但他的優點依舊不可替代。

死鎖和臟數據就是典型的線程安全問題。

簡單來說,線程安全就是:在多線程環境中,能永遠保證程序的正確性。

只有存在共享數據時才需要考慮線程安全問題。

java內存區域:

其中,方法區和堆就是主要的線程共享區域。那麼就是說共享對象只可能是類的屬性域或靜態域。

了解了線程安全問題的一些基本概念後, 我們就來說說如何解決線程安全問題。我們來從一個簡單的servlet示例來分析:

1. 了解業務場景的線程模型

這里的線程模型指的是: 在該業務場景下, 可能出現的線程調用實況。

眾所周知,Servlet是被設計為單實例,在請求進入tomcat後,由Connector建立連接,再講請求分發給內部線程池中的Processor,

此時Servlet就處於一個多線程環境。即如果存在幾個請求同時訪問某個servlet,就可能會有幾個線程同時訪問該servlet對象。如圖:

線程模型,如果簡單的話,就在腦海模擬一下就好了,復雜的話就可以用紙筆或其他工具畫出來。

2. 找出共享對象

這里的共享對象就很明顯就是ReqCounterServlet。

3. 分析共享對象的不變性條件

不變性條件,這個名詞是在契約式編程的概念中的。不變性條件保證類的狀態在任何功能被執行後都保持在一個可接受的狀態。

這里可以引申出,不可變對象是線程安全的。(因為不可變對象就沒有不變性條件)

不變性條件則主要由對可變狀態的修改與訪問構成。

這里的servlet很簡單, 不變性條件大致可以歸納為: 每次請求進入時count計數必須加一,且計數必須正確。

在復雜的業務中, 類的不變性條件往往很難考慮周全。設計的世界是險惡的,只能小心謹慎,用測量去證明,最大程度地減少錯誤出現的幾率。

4. 用特定的策略解決線程安全問題。

如何解決的確是該流程的重點。目前分三種方式解決:

第一種,修改線程模型。即不在線程之間共享該狀態變數。一般這個改動比較大,需要量力而行。

第二種,將對象變為不可變對象。有時候實現不了。

第三種,就比較通用了,在訪問狀態變數時使用同步。 synchronized和Lock都可以實現同步。簡單點說,就是在你修改或訪問可變狀態時加鎖,獨占對象,讓其他線程進不來。

這也算是一種線程隔離的辦法。(這種方式也有不少缺點,比如說死鎖,性能問題等等)

其實有一種更好的辦法,就是設計線程安全類。《代碼大全》就有提過,問題解決得越早,花費的代價就越小。

是的,在設計時,就考慮線程安全問題會容易的多。

首先考慮該類是否會存在於多線程環境。如果不是,則不考慮線程安全。

然後考慮該類是否能設計為不可變對象,或者事實不可變對象。如果是,則不考慮線程安全

最後,根據流程來設計線程安全類。

設計線程安全類流程:

1、找出構成對象狀態的所有變數。

2、找出約束狀態變數的不變性條件。

3、建立對象狀態的並發訪問管理策略。

有兩種常用的並發訪問管理策略:

1、java監視器模式。 一直使用某一對象的鎖來保護某狀態。

2、線程安全委託。將類的線程安全性委託給某個或多個線程安全的狀態變數。(注意多個時,這些變數必須是彼此獨立,且不存在相關聯的不變性條件。)

10. Java Swing開發中的線程安全

SwingAPI的設計目標是強大 靈活和易用 非凡地 我們希望能讓程序員們方便地建立新的Swing組件 不論是從頭開始還是通過擴展我們所提供的一些組件 出於這個目的 我們不要求Swing組件支持多線程訪問 相反 我們向組件發送請求並在單一慎李線程中執行請求 本文討論線程和Swing組件 目的不僅是為了幫助你以線程安全的方式使用SwingAPI 而且解釋了我們為什麼會選擇現在這樣的線程方案 本文包括以下內容

單線程規則 Swing線程在同一時刻僅能被一個線程所訪問 一般來說 這個線程是事件派發線程 規則的例外 有些操作保證是線程安全的 事件分發 假如你需要從事件處理或繪制代碼以外的地方訪問UI 那麼你可以使用SwingUtilities類的invokeLater要求在事件派發線程中執行某些代碼 這個方法會立即返回 不會等待代碼執行完畢 invokeAndWait行為與invokeLater類似 除了這個方法會等待代碼執行完畢 一般地 你可以用invokeLater來代替這個方法 下面是一些使用這幾個API的例子 請同時參閱《TheJavaTutorial》中的 BINGOexample 尤其是以下幾個類 CardWindow ControlPane Player和OverallStatusPane

使用invokeLater方法你可以從任何線程調用invokeLater方法以請求事件派發線程運行特定代碼 你必須把要運行的代碼放到一個Runnable對象的run方法中 並將此Runnable對象設為invokeLater的參數 invokeLater方法會立即返回 不等待事件派發線程執行指定代碼 這是一個使用invokeLater方法的例子

RunnabledoWorkRunnable=newRunnable };

SwingUtilities invokeLater;使用invokeAndWait方法invokeAndWait方法和invokeLater方法很相似 除了invokeAndWait方法會等事件派發線程執行了指定代碼才返回 在可能的情況下 你應該盡量用invokeLater來代替invokeAndWait 假如你真的要使用invokeAndWait 請確保調用invokeAndWait的線程不會在調用期間持有任何其他線程可能需要的鎖

這是一個使用invokeAndWait的例子

}; 褲滑SwingUtilities invokeAndWait; }

類似地 假設一個線程需要對GUI的狀態進行存取 比如文本域的內容 它的代碼可能類似這樣

voidprintTextField throwsException }; SwingUtilities invokeAndWait; System out println;}

假如你能避免使用線程 最好這樣做 線程可能難於使用 並使得程序的debug更困難 一般來說 對於嚴格意義下的GUI工作 線程是不必要的 比如對組件屬性的更新 不管怎麼說 有時候線程是必要的 下列情況是使用線程的一些典型情況 執行一項費時的任務而不必將事件派發線程鎖定 例子包括執行大量計算的情況 會導致大量類被裝載的情況 和為網路或磁碟I/O而阻塞的情況 重復地執行一項操作 通常在兩次操作間間隔一個預定的時間周期 要等待來自客戶的消息 你可以使用兩個類來幫助你實現線程 SwingWorker 創建一個後台線程來執行費時的操作 Timer 創建一個線程來執行或多次執行某些代碼 在兩次執行間間隔用戶定義的延遲 使用SwingWorker類SwingWorker類在SwingWorker java中實現 這個類並不包寬純遲含在Java的任何發行版中 所以你必須單獨下載它 SwingWorker類做了所有實現一個後台線程所需的骯臟工作 雖然許多程序都不需要後台線程 後台線程在執行費時的操作時仍然是很有用的 它能提高程序的性能觀感

要使用SwingWorker類 你首先要實現它的一個子類 在子類中 你必須實現construct方法還包含你的長時間操作 當你實例化SwingWorker的子類時 SwingWorker創建一個線程但並不啟動它 你要調用你的SwingWorker對象的start方法來啟動線程 然後start方法會調用你的construct方法 當你需要construct方法返回的對象時 可以調用SwingWorker類的get方法 這是一個使用SwingWorker類的例子

//在main方法中 finalSwingWorkerworker=newSwingWorker }; worker start; //在動作事件處理方法中 JOptionPane showMessageDialog)

當程序的main方法調用start方法 SwingWorker啟動一個新的線程來實例化ExpensiveDialogComponent main方法還構造了由一個窗口和一個按鈕組成的GUI 當用戶點擊按鈕 程序將阻塞 假如必要 阻塞到ExpensiveDialogComponent創建完成 然後程序顯示一個包含ExpensiveDialogComponent的模式對話框 你可以在MyApplication java找到整個程序 使用Timer類Timer類通過一個ActionListener來執行或多次執行一項操作 你創建定時器的時候可以指定操作執行的頻率 並且你可以指定定時器的動作事件的監聽者 啟動定時器後 動作監聽者的actionPerformed方法會被調用來執行操作 定時器動作監聽者定義的actionPerformed方法將在事件派發線程中調用 這意味著你不必在其中使用invokeLater方法 這是一個使用Timer類來實現動畫循環的例子

publicvoidstartAnimationelse } publicvoidstopAnimation publicvoidactionPerformed }

在一個線程中執行所有的用戶界面代碼有這樣一些優點 組件開發者不必對線程編程有深入的理解 像ViewPoint和Trestle這類工具包中的所有組件都必須完全支持多線程訪問 使得擴展非常困難 尤其對不精通線程編程的開發者來說 最近的一些工具包如SubArctic和IFC 都採用和Swing類似的設計 事件以可預知的次序派發 invokeLater排隊的runnable對象從滑鼠和鍵盤事件 定時器事件 繪制請求的同一個隊列派發 在一些組件完全支持多線程訪問的工具包中 組件的改變被變化無常的線程調度程序穿插到事件處理過程中 這使得全面測試變得困難甚至不可能 更低的代價 嘗試小心鎖住臨界區的工具包要花費實足的時間和空間在鎖的治理上 每當工具包中調用某個可能在客戶代碼中實現的方法時 工具包都要保存它的狀態並釋放所有鎖 以便客戶代碼能在必要時獲得鎖 當控制權交回到工具包 工具包又必須重新抓住它的鎖並恢復狀態 所有應用程序都不得不負擔這一代價 即使大多數應用程序並不需要對GUI的並發訪問 這是的SubArcticJavaToolkit的對在工具包中支持多線程訪問的問題的描述 我們的基本信條是 當設計和建造多線程應用程序 尤其是那些包括GUI組件的應用程序時 必須保證極端小心 線程的使用可能會很有欺騙性 在許多情況下 它們表現得能夠極好的簡化編成 使得設計 專注於單一任務的簡單自治實體 成為可能 在一些情況下它們的確簡化了設計和編碼 然而 在幾乎所有的情況下 它們都使得調試 測試和維護的困難大大增加甚至成為不可能 無論大多數程序員所受的練習 他們的經驗和實踐 還是我們用來幫助自己的工具 都不是能夠用來對付非決定論的 例如 全面測試在bug依靠於時間時是幾乎不可能的 尤其對於Java來說 一個程序要運行在許多不同類型的機器的操作系統平台上 並且每個程序都必須在搶先和非搶先式調度下都能正常工作 由於這些固有的困難 我們力勸你三思是否絕對有使用線程的必要 盡管如此 有些情況下使用線程是必要的 所以subArctic提供了一個線程安全的訪問機制

lishixin/Article/program/Java/gj/201311/27616

閱讀全文

與java線程安全的方法相關的資料

熱點內容
桌面雲配置文件分離 瀏覽:505
iphone5如何升級4g網路 瀏覽:5
團購是在哪個app 瀏覽:897
打開多個word文檔圖片就不能顯示 瀏覽:855
騰訊新聞怎麼切換版本 瀏覽:269
app安裝失敗用不了 瀏覽:326
桌面文件滑鼠點開會變大變小 瀏覽:536
手機誤刪系統文件開不了機 瀏覽:883
微信兔子甩耳朵 瀏覽:998
android藍牙傳文件在哪裡 瀏覽:354
蘋果6s軟解是真的嗎 瀏覽:310
c語言代碼量大 瀏覽:874
最新網路衛星導航如何使用 瀏覽:425
以下哪些文件屬於圖像文件 瀏覽:774
zycommentjs 瀏覽:414
確認全血細胞減少看哪些數據 瀏覽:265
文件有哪些要求 瀏覽:484
cad打開時會出現兩個文件 瀏覽:65
什麼是轉基因網站 瀏覽:48
手柄設備有問題代碼43 瀏覽:921

友情鏈接