java線程安全
A. 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、線程安全委託。將類的線程安全性委託給某個或多個線程安全的狀態變數。(注意多個時,這些變數必須是彼此獨立,且不存在相關聯的不變性條件。)
B. Java中如何使方法線程安全
線程不安全的場合很多,比如像操作系統中的用戶界面、列印機等外設、控制台輸出,都不允許並發(設想兩個程序同時要輸出文字到同一個屏幕,那還不亂套了)
在代碼中,每個線程有自己的堆棧但是共享整個堆,所以訪問那些全局的變數,也必須同步,否則會出現臟讀數據。
同步也不是萬能的良葯。不當的鎖定會導致程序死鎖,而且多線程本身就是為了提高性能,但是同步使用多了,程序又實質上退化成了單線程程序,用多線程的意義也就沒了。
C. java里線程安全是什麼意思有什麼作用
比如說,兩個線程操作同一個ArrayList變數,那麼一個線程這一時刻讀的數據可能在下一刻要改變。
一般在類似於下面的情景下考慮線程安全的問題:
ArrayList procts=new ArrayList ();
procts用來存放生產出來的產品。
現在假設:有3個消費者線程,2個生產者線程。
每個生產者線程生產出一個產品,執行
procts.add(new Proct());
每個消費者線程消費一個產品執行
if(procts.size()>=1){ procts.remove(0);}
如果procts里現在只有一個產品可以消費,但是有2個消費者線程請求消費,那麼就有可能出現一個產品被同時消費的問題,而這是和實際不符的。
但是不同的線程訪問Vector的時候不會發生這種錯誤,因為java會有相應的機制是同一時刻只有一個線程對這個變數操作。
這就是所謂的:
Vector:是線程安全的
ArrayList:不是線程安全的
D. java中哪些線程安全
JAVA中線程安全的map有:Hashtable、synchronizedMap、ConcurrentHashMap。
java中map中線程安全怎麼實現:
1、同步的map就是Hashtable, concurrenthashmap。
2、你看到的Hashtable就是直接在hashmap上加了個鎖,concurrenthashmap就是分成多個分段鎖。
java代碼中線程安全級別:
1、絕對線程安全。
在任何環境下,調用者都不需要考慮額外的同步措施,都能夠保證程序的正確性。這個定義要求很嚴格,java裡面滿足這個要求的類比較少,對於實現jsr133規范(java內存模型)的jdk(一般指jdk5.0之上),一般的不變類都是滿足絕地線程安全的。比如 String,Integer類。一般情況下,定義了如果一個類裡面所有欄位都是final類型的,一般都認為這個類是不變的。不變類都是絕對線程安全的。
2、相對線程安全
在一般情況下,調用者都不需要考慮線程同步,大多數情況下,都能夠正常運行。jdk裡面大多數類都是相對安全的。最常見的例子是java裡面Vector類。
E. java中的線程安全問題
通常是指線程間,操作共用的變數,不會造成混亂。
加了 synchronized 是讓線程鎖定、別的線程等待
F. java中的線程安全是什麼什麼叫線程安全什麼叫不安全
java中的線程安全是什麼:
就是線程同步的意思,就是當一個程序對一個線程安全的方法或者語句進行訪問的時候,其他的不能再對他進行操作了,必須等到這次訪問結束以後才能對這個線程安全的方法進行訪問
什麼叫線程安全:
如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程運行的結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是線程安全的。
或者說:一個類或者程序所提供的介面對於線程來說是原子操作或者多個線程之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。
線程安全問題都是由全局變數及靜態變數引起的。
若每個線程中對全局變數、靜態變數只有讀操作,而無寫操作,一般來說,這個全局變數是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。
G. Java中的類如何保證線程安全
java中,線程安全的解決方法或過程:
1.如果對象是immutable,則是線程安全的,例如:String,可以放心使用。
2. 如果對象是線程安全的,則放心使用。
3.有條件線程安全,對於Vector和Hashtable一般情況下是線程安全的,但是對於某些特殊情況,需要通過額外的synchronized保證線程安全。
4.使用synchronized關鍵字。
H. 在java中什麼是線程安全的
ArrayList和HashMap是非同步的,
Vector和HashTable是同步的
List本身是一個介面,有一個類叫Vector,它實現了List,並且這個Vector是線程同步的。 在實際運用中,至少我沒看見誰直接用List來裝東西,最多會是這樣:
List list = new ArrayList();
List list = new Vector();
I. java什麼叫線程安全
Count.java:
[java]view plainprint?
publicclassCount{
privateintnum;
publicvoidcount(){
for(inti=1;i<=10;i++){
num+=i;
}
System.out.println(Thread.currentThread().getName()+"-"+num);
}
}
- 在這個類中的count方法是計算1一直加到10的和,並輸出當前線程名和總和,我們期望的是每個線程都會輸出55。
publicclassThreadTest{
publicstaticvoidmain(String[]args){
Runnablerunnable=newRunnable(){
Countcount=newCount();
publicvoidrun(){
count.count();
}
};
for(inti=0;i<10;i++){
newThread(runnable).start();
}
}
}
- 這里啟動了10個線程,看一下輸出結果:
Thread-0-55
Thread-1-110
Thread-2-165
Thread-4-220
Thread-5-275
Thread-6-330
Thread-3-385
Thread-7-440
Thread-8-495
Thread-9-550
- 只有Thread-0線程輸出的結果是我們期望的,而輸出的是每次都累加的,這里累加的原因以後的博文會說明,那麼要想得到我們期望的結果,有幾種解決方案:
publicclassCount{
publicvoidcount(){
intnum=0;
for(inti=1;i<=10;i++){
num+=i;
}
System.out.println(Thread.currentThread().getName()+"-"+num);
}
}
publicclassThreadTest4{
publicstaticvoidmain(String[]args){
Runnablerunnable=newRunnable(){
publicvoidrun(){
Countcount=newCount();
count.count();
}
};
for(inti=0;i<10;i++){
newThread(runnable).start();
}
}
}
ThreadTest.java:
[java]view plainprint?
[java]view plainprint?
1. 將Count中num變成count方法的局部變數;
[java]view plainprint?
2. 將線程類成員變數拿到run方法中,這時count引用是線程內的局部變數;
[java]view plainprint?
3. 每次啟動一個線程使用不同的線程類,不推薦。
上述測試,我們發現,存在成員變數的類用於多線程時是不安全的,不安全體現在這個成員變數可能發生非原子性的操作,而變數定義在方法內也就是局部變數是線程安全的。想想在使用struts1時,不推薦創建成員變數,因為action是單例的,如果創建了成員變數,就會存在線程不安全的隱患,而struts2是每一次請求都會創建一個action,就不用考慮線程安全的問題。所以,日常開發中,通常需要考慮成員變數或者說全局變數在多線程環境下,是否會引發一些問題。
J. java線程安全性問題
//下次發源代碼時先整理好再發出來,累死了~呼呼
packaget;
importjava.io.File;
importjava.io.IOException;
importjava.util.Iterator;
importjava.util.List;
importjavax.servlet.ServletConfig;
importjavax.servlet.ServletContext;
importjavax.servlet.ServletException;
importjavax.servlet.http.HttpServlet;
importjavax.servlet.http.HttpServletRequest;
importjavax.servlet.http.HttpServletResponse;
/**
*先幫你整理下,你這樣估計沒幾人願意看:
*
*@authorHuangYong
*
*/
{
//驗證版本是否一致的java身份證
=-5318854875823790516L;
//上傳的組件中要運用到的對象
privateServletContextsc;
//保存路徑
privateStringsavePath;
publicvoiddoGet(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
doPost(request,response);
}
publicvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse)
throwsServletException,IOException{
//解決中文亂碼問題,後面可以看看過濾器
request.setCharacterEncoding("UTF-8");
//設計模式中的工廠模式
DiskFileItemFactoryfactory=newDiskFileItemFactory();
//用來在Servlet中完成文件
ServletFileUploapload=newServletFileUpload(factory);
try{
Listitems=upload.parseRequest(request);
Iteratorit=items.iterator();
while(it.hasNext()){
//包括傳過來的所有信息,包括文件和普通文本信息
FileItemitem=(FileItem)it.next();
//這個方法判斷信息類型,普通文本為true文件為false
if(item.isFormField()){
System.out.println("上傳的表單項的名稱:"+item.getFieldName());
System.out.println("內容:"+item.getString("UTF-8"));
}else{
if(item.getName()!=null&&!item.getName().equals("")){
System.out.println("上傳文件的文件大小:"+item.getSize());
System.out
.println("上傳文件的文件類型:"+item.getContentType());
System.out.println("上傳文件的文件名字:"+item.getName());
Filetempfile=newFile(item.getName());
//sc.getRealPath("/")代表獲得當前應用的物理路徑
//,savePath為上傳文件保存在伺服器上的地址,自己在web.xml下設好的
Filefile=newFile(sc.getRealPath("/")+savePath
+tempfile.getName());
//將上傳的文件保存在伺服器的物理磁碟目錄下
item.write(file);
//在jsp頁面上現實上傳的結果
}
}
}
//解析文件
}catch(Exceptione){
thrownewRuntimeException(e);
}
}
publicvoidinit(ServletConfigconfig)throwsServletException{
//這是自己設的參數,用於保存上傳文件應當保存的目錄地址,在xml文件中初始化
savePath=config.getInitParameter("savePath");
sc=config.getServletContext();
}
}
首先, 成員變數用private修飾就是為了不讓外部程序隨意訪問, 如果需要暴露給外部使用時, 通常需要getter訪問器來控制許可權, 框架程序除外;
關於成員變數引起的線程安全問題根據需求來控制, 如果沒有必要時直接定義在方法體內就沒有線程安全問題了, 象JDBC的對象(Connection, PreparedStatement, ResultSet..)直接在方法體內部定義;
sc 和 savePath 正常情況下沒有線程安全問題, 不過你存儲上傳的文件時要小心同名文件問題
希望能幫助你