1. java的collection中哪些是線程安全的
java集合框架提供了多種synchronized集合, 比如Vector, HashTable, Collections的synchronizedXxx方法的返回值等.
synchronized集合是線程安全的, 但不是嚴格線程安全的. 根據JCIP第二章關於線程安全的定義--線程安全的類無需調用方進行額外的同步--synchronized集合是不滿足該定義的. 如果我們將線程安全的定義放寬一些--單次調用對象的方法而無需調用方進行額外的同步, 這樣synchronized集合就符合定義了.
2. Java集合中哪些類是線程安全的
線程安全類
在集合框架中,有些類是線程安全的,這些都是jdk1.1中的出現的。在jdk1.2之後,就出現許許多多非線程安全的類。 下面是這些線程安全的同步的類:
vector:就比arraylist多了個同步化機制(線程安全),因為效率較低,現在已經不太建議使用。在web應用中,特別是前台頁面,往往效率(頁面響應速度)是優先考慮的。
statck:堆棧類,先進後出
hashtable:就比hashmap多了個線程安全
enumeration:枚舉,相當於迭代器
除了這些之外,其他的都是非線程安全的類和介面。
線程安全的類其方法是同步的,每次只能一個訪問。是重量級對象,效率較低。
其他:
1. hashtable跟hashmap的區別
hashtable是線程安全的,即hashtable的方法都提供了同步機制;hashmap不是線程安全的,即不提供同步機制 ;hashtable不允許插入空值,hashmap允許!
2. 多線程並發修改一 個 集合 怎麼辦
用老的Vector/Hashtable類
StringBuffer是線程安全,而StringBuilder是線程不安全的。對於安全與不安全沒有深入的理解情況下,易造成這樣的錯覺,如果對於StringBuffer的操作均是線程安全的,然而,Java給你的保證的線程安全,是說它的方法是執行是排它的,而不是對這個對象本身的多次調用情況下,還是安全的。看看下邊的例子,在StringBufferTest中有一個數據成員contents它是用來擴展的,它的每一次append是線程安全的,但眾多次append的組合並不是線程安全的,這個輸出結果不是太可控的,但如果對於log和getContest方法加關鍵字synchronized,那麼結果就會變得非常條理,如果換成StringBuider甚至是append到一半,它也會讓位於其它在此基礎上操作的線程:
3. java線程安全的集合不能保證線程安全
僅能保證單個操作是線程安全的,但是迭代器則不再此列。
另外「組合操作」也是不能保證安全的。
如
if(proct.size()<10){
proct.add("obj");
}
不能保證proct.size()之後,proct.add("obj")之前,另外一個線程不add別的數據。
也不能保證
if (proct.contains("apple")) {
int index = proct.indexOf("apple");
proct.set(index, "banana");
}
確認有apple,就能查到apple的下標,即便是查到有效的下標,也不能保證set時,該下標仍然有效。
4. 如何保證集合是線程安全的
並發(concurrency)一個並不陌生的詞,簡單來說,就是cpu在同一時刻執行多個任務。
而Java並發則由多線程實現的。
在jvm的世界裡,線程就像不相乾的平行空間,串列在虛擬機中。(當然這是比較籠統的說法,線程之間是可以交互的,他們也不一定是串列。)
多線程的存在就是壓榨cpu,提高程序性能,還能減少一定的設計復雜度(用現實的時間思維設計程序)。
這么說來似乎線程就是傳說中的銀彈了,可事實告訴我們真正的銀彈並不存在。
多線程會引出很多難以避免的問題, 如死鎖,臟數據,線程管理的額外開銷,等等。更大大增加了程序設計的復雜度。
但他的優點依舊不可替代。
死鎖和臟數據就是典型的線程安全問題。
簡單來說,線程安全就是: 在多線程環境中,能永遠保證程序的正確性。
只有存在共享數據時才需要考慮線程安全問題。
java內存區域:
其中, 方法區和堆就是主要的線程共享區域。那麼就是說共享對象只可能是類的屬性域或靜態域。
了解了線程安全問題的一些基本概念後, 我們就來說說如何解決線程安全問題。我們來從一個簡單的servlet示例來分析:public class ReqCounterServlet extends HttpServlet{ private int count = 0;
public void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
count++;
System.out.print("當前已達到的請求數為" + count);
}
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException { // ignore }
}1. 了解業務場景的線程模型
這里的線程模型指的是: 在該業務場景下, 可能出現的線程調用實況。
眾所周知,Servlet是被設計為單實例,在請求進入tomcat後,由Connector建立連接,再講請求分發給內部線程池中的Processor,
此時Servlet就處於一個多線程環境。即如果存在幾個請求同時訪問某個servlet,就可能會有幾個線程同時訪問該servlet對象。如圖:
線程模型,如果簡單的話,就在腦海模擬一下就好了,復雜的話就可以用紙筆或其他工具畫出來。
2. 找出共享對象
這里的共享對象就很明顯就是ReqCounterServlet。
3. 分析共享對象的不變性條件
不變性條件,這個名詞是在契約式編程的概念中的。不變性條件保證類的狀態在任何功能被執行後都保持在一個可接受的狀態。
這里可以引申出, 不可變對象是線程安全的。(因為不可變對象就沒有不變性條件)
不變性條件則主要由對可變狀態的修改與訪問構成。
這里的servlet很簡單, 不變性條件大致可以歸納為: 每次請求進入時count計數必須加一,且計數必須正確。
在復雜的業務中, 類的不變性條件往往很難考慮周全。設計的世界是險惡的,只能小心謹慎,用測量去證明,最大程度地減少錯誤出現的幾率。
4. 用特定的策略解決線程安全問題。
如何解決的確是該流程的重點。目前分三種方式解決:
第一種,修改線程模型。即不在線程之間共享該狀態變數。一般這個改動比較大,需要量力而行。
第二種,將對象變為不可變對象。有時候實現不了。
第三種,就比較通用了,在訪問狀態變數時使用同步。 synchronized和Lock都可以實現同步。簡單點說,就是在你修改或訪問可變狀態時加鎖,獨占對象,讓其他線程進不來。
這也算是一種線程隔離的辦法。(這種方式也有不少缺點,比如說死鎖,性能問題等等)
其實有一種更好的辦法,就是設計線程安全類。《代碼大全》就有提過,問題解決得越早,花費的代價就越小。
是的,在設計時,就考慮線程安全問題會容易的多。
首先考慮該類是否會存在於多線程環境。如果不是,則不考慮線程安全。
然後考慮該類是否能設計為不可變對象,或者事實不可變對象。如果是,則不考慮線程安全
最後,根據流程來設計線程安全類。
設計線程安全類流程:
1、找出構成對象狀態的所有變數。
2、找出約束狀態變數的不變性條件。
3、建立對象狀態的並發訪問管理策略。
有兩種常用的並發訪問管理策略:
1、java監視器模式。 一直使用某一對象的鎖來保護某狀態。
2、線程安全委託。 將類的線程安全性委託給某個或多個線程安全的狀態變數。(注意多個時,這些變數必須是彼此獨立,且不存在相關聯的不變性條件。)
5. 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的集合工具類在添加方面更加快一些,其他方法這里篇幅關系就不測試了,大家有興趣去試一下。
6. java線程安全的集合還要加鎖嗎
線程安全是指要控制多個線程對某個資源的有序訪問或修改,而在這些線程之間沒有產生沖突。 在Java里,線程安全一般體現在兩個方面: 1、多個thread對同一個java實例的訪問(read和modify)不會相互干擾,它主要體現在關鍵字synchronized。
7. Java哪些集合類是線程安全的
在集合框架中,有些類是線程安全的,這些都是jdk1.1中的出現的。在jdk1.2之後,就出現許許多多非線程安全的類。 下面是這些線程安全的同步的類:
vector:就比arraylist多了個同步化機制(線程安全),因為效率較低,現在已經不太建議使用。在web應用中,特別是前台頁面,往往效率(頁面響應速度)是優先考慮的。
statck:堆棧類,先進後出
hashtable:就比hashmap多了個線程安全
enumeration:枚舉,相當於迭代器
除了這些之外,其他的都是非線程安全的類和介面。
線程安全的類其方法是同步的,每次只能一個訪問。是重量級對象,效率較低。
其他:
1. hashtable跟hashmap的區別
hashtable是線程安全的,即hashtable的方法都提供了同步機制;hashmap不是線程安全的,即不提供同步機制 ;hashtable不允許插入空值,hashmap允許!
8. java集合類中哪些是線程安全的
一些歷史類的一般是線程安全的,例如:Vector,HashTable等
在jdk升級後,出現了替代一些集合的類,ArrayList,HashMap等,一般都是線程不安全的。
9. 如何保證集合是線程安全的
1、不可變
在java語言中,不可變的對象一定是線程安全的,無論是對象的方法實現還是方法的調用者,都不需要再採取任何的線程安全保障措施。如final關鍵字修飾的數據不可修改,可靠性最高。
2、絕對線程安全
絕對的線程安全完全滿足Brian GoetZ給出的線程安全的定義,這個定義其實是很嚴格的,一個類要達到「不管運行時環境如何,調用者都不需要任何額外的同步措施」通常需要付出很大的代價。
3、相對線程安全
相對線程安全就是我們通常意義上所講的一個類是「線程安全」的。
它需要保證對這個對象單獨的操作是線程安全的,我們在調用的時候不需要做額外的保障措施,但是對於一些特定順序的連續調用,就可能需要在調用端使用額外的同步手段來保證調用的正確性。
在java語言中,大部分的線程安全類都屬於相對線程安全的,例如Vector、HashTable、Collections的synchronizedCollection()方法保證的集合。
4、線程兼容
線程兼容就是我們通常意義上所講的一個類不是線程安全的。
線程兼容是指對象本身並不是線程安全的,但是可以通過在調用端正確地使用同步手段來保證對象在並發環境下可以安全地使用。Java API中大部分的類都是屬於線程兼容的。如與前面的Vector和HashTable相對應的集合類ArrayList和HashMap等。
5、線程對立
線程對立是指無論調用端是否採取了同步錯誤,都無法在多線程環境中並發使用的代碼。由於java語言天生就具有多線程特性,線程對立這種排斥多線程的代碼是很少出現的。
一個線程對立的例子是Thread類的supend()和resume()方法。如果有兩個線程同時持有一個線程對象,一個嘗試去中斷線程,另一個嘗試去恢復線程,如果並發進行的話,無論調用時是否進行了同步,目標線程都有死鎖風險。正因此如此,這兩個方法已經被廢棄啦。