Java靜態static工具類執行緒安全問題研究
針對靜態方法有以下一些前提:
- 靜態方法和例項方法的區別是靜態方法只能引用靜態變數,靜態方法通過類名來呼叫,例項方法通過物件例項來呼叫
- 每個執行緒都有自己的執行緒棧,棧與執行緒同時建立,每一個虛擬機器執行緒都有自己的程式計數器PC,在任何時刻,一個虛擬機器執行緒只會執行一個方法的程式碼,這個方法稱為該執行緒的當前方法,如果這個方法不是native的,程式計數器就儲存虛擬機器正在執行的位元組碼指令的地址。
- 執行緒呼叫方法的時候會建立棧幀,用於儲存區域性變數表和運算元棧以及指向該類常量池的引用
- 靜態方法雖然是同一個方法,但是不同執行緒在呼叫,程式計數器的值是不一樣的,操作這兩個執行緒不會相互影響(假設不存在訪問共享變數的情況)
在設計工具類時,這要沒有共享的變數,靜態工具類方法不需要加鎖。在使用單例模式做工具類,這個時候靜態方法就需要加鎖,因為所有的執行緒雖然都是有自己的方法棧,但是在方法棧中操作的是同一個物件的實體(所以需要加鎖,加鎖的代價是所有的執行緒需要等待鎖的釋放才能使用該物件的引用)在使用多例模式做工具類時,這個時候也是不需要加鎖,因為所有的執行緒都有自己的方法棧,但是方法棧幀中建立了獨立的物件引用,每個執行緒都是在操作各自方法棧幀中的區域性物件引用,所以這時候不要同步。
由於web天生併發性,導致我們的一般java工具類會在這樣的環境下出現問題。
其實問題的根源就是我們的工具類不是執行緒安全的。
有一個生成md5的工具類:
public class MD5 { private static long[] state = new long[4]; private static long[] count = new long[2]; private static byte[] buffer = new byte[64]; private static byte[] digest = new byte[16]; private String digestHexStr=""; public static MD5() { } //計算MD5 public static String getMD5ofStr(String inbuf) { } }
變數state, count ,buffer ,digest 演算法中用到的核心資料,digestHexStr存放計算的結果。在多執行緒併發訪問的情況下,這些變數是會被“共享”的,所以會導致計算結果不準確甚至出現異常。
有三種比較簡單的方法可以解決:
getMD5ofStr方法變成非static的普通方法,這樣每次呼叫這個方法都必須new一個新的MD5物件。
getMD5ofStr方法變成同步方法(同步程式碼塊,顯示鎖,synchronized method都可以)。
將被“共享”的變數放到方法getMD5ofStr裡面,不設定成員變數。
考慮到現在系統有些地方已經開始使用這個工具類了,不便改動結構,先採用第二種快速修復bug,然後騰出時間用第三種發放重構。
PS:
工具類能否設計成單例?如果能最好。單例能減少建立類和分配記憶體的開銷,減少垃圾回收次數。
工具類能否設計成不變類?如果能最好,不變類天生執行緒安全!
在併發環境下,工具類能不能不用同步?不管怎麼說,同步都是要有一些開銷的。
PPS:
這樣會好一些:
public final class MD5 { private MD5(){} //計算MD5 public static String getMD5ofStr(String inbuf) { long[] state = new long[4]; long[] count = new long[2]; byte[] buffer = new byte[64]; byte[] digest = new byte[16]; String digestHexStr=""; ........ } }
其它示例:
public class StaticTest { private static int count = 0; private static int counts = 0; /** * 不會存在併發問題 * * @return */ public static String getTestStr() { String xx = Thread.currentThread().toString(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return xx; } /** * 存不存在併發問題與傳入的變數有關 * 假如thread a和thread b都在操作物件a則存在 * @param user * @return */ public static String getTestUser(User user) { String str = "id: " + user.getId() + "name: " + user.getName(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return str; } /** * 存在併發問題 * * @return */ public static int getTestCount() { count++; count++; count++; count++; count++; try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } count++; count++; count++; count++; count++; return count; } /** * 不存在併發問題 * * @return */ public synchronized static int getTestCountS() { counts++; counts++; counts++; counts++; counts++; try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } counts++; counts++; counts++; counts++; counts++; return counts; } public static void main(String[] args) { User user = new User(); for (int i = 0 ; i < 1000 ; i++){ final int finalI = i; Thread thread = new Thread(new Runnable() { @Override public void run() { User userTmp = new User(); user.setId(finalI); user.setName(Thread.currentThread().toString()); userTmp.setId(finalI); userTmp.setName(Thread.currentThread().toString()); //區域性變數不存在問題 System.out.println("getTestStr: " + Thread.currentThread() + StaticTest.getTestStr()); //與user有關 System.out.println("getTestUser: " + Thread.currentThread() + StaticTest.getTestUser(user)); System.out.println("getTestUseS: " + Thread.currentThread() + StaticTest.getTestUser(userTmp)); //執行緒不安全 System.out.println("getTestCount: " + Thread.currentThread() + StaticTest.getTestCount() % 10); //安全但是慢需要加鎖 System.out.println("getTestCountS: " + Thread.currentThread() + StaticTest.getTestCountS() % 10); } }); thread.start(); } } }
參考:
http://blog.csdn.net/thekenofdis/article/details/74529886(以上內容部分轉自此篇文章)
https://www.cnblogs.com/LvLoveYuForever/p/6077148.html
http://www.genshuixue.com/i-cxy/p/7637981
http://wuhaocn.iteye.com/blog/2269391(以上內容部分轉自此篇文章)
==>如有問題,請聯絡我:easonjim#163.com,或者下方發表評論。<==
相關文章
- 深入JAVA執行緒安全問題Java執行緒
- Java多執行緒中執行緒安全與鎖問題Java執行緒
- java執行緒安全問題之靜態變數、例項變數、區域性變數Java執行緒變數
- Java | 靜態巢狀類(Static Nested Class)Java巢狀
- Java 執行緒安全問題的本質Java執行緒
- 03 執行緒安全問題執行緒
- SimpleDateFormat 執行緒安全問題ORM執行緒
- 靜態內部類實現的單例模式是執行緒安全的單例模式執行緒
- Java執行緒的併發工具類Java執行緒
- ArrayList 的執行緒安全問題執行緒
- 多執行緒,你覺得你安全了?(執行緒安全問題)執行緒
- java 執行緒安全問題,解決執行緒安全問題——同步程式碼塊,同步方法,Lock鎖,Object類中wait方法,notify方法。等待喚醒案例。Java執行緒ObjectAI
- Java併發-執行緒安全的集合類Java執行緒
- Java中解決多執行緒資料安全問題Java執行緒
- 詳解JAVA執行緒問題診斷工具Thread DumpJava執行緒thread
- Java多執行緒同步工具類之SemaphoreJava執行緒
- Java多執行緒同步工具類之CyclicBarrierJava執行緒
- Java多執行緒同步工具類之CountDownLatchJava執行緒CountDownLatch
- Java之執行緒安全問題的3種處理方式(通過執行緒同步)Java執行緒
- Java多執行緒-執行緒狀態Java執行緒
- parallelStream中的執行緒安全問題Parallel執行緒
- Java執行緒安全Java執行緒
- java的執行緒、建立執行緒的 3 種方式、靜態代理模式、Lambda表示式簡化執行緒Java執行緒模式
- Java執行緒(一):執行緒安全與不安全Java執行緒
- 啃碎併發(五):Java執行緒安全特性與問題Java執行緒
- Java執行緒安全的集合類:Map、List、SetJava執行緒
- 05.java多執行緒問題Java執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- 模板方法中的執行緒安全問題執行緒
- lambda中stream執行緒安全的問題執行緒
- 從FMDB執行緒安全問題說起執行緒
- 單例模式執行緒安全reorder問題單例模式執行緒
- Java併發專題(二)執行緒安全Java執行緒
- java執行緒實現的三種方式以及靜態代理Java執行緒
- Java 多執行緒基礎(四)執行緒安全Java執行緒
- 【重學Java】多執行緒進階(執行緒池、原子性、併發工具類)Java執行緒
- java多執行緒與併發 - 併發工具類Java執行緒
- 靜態變數在多執行緒環境下的初始化是執行緒安全的嗎?變數執行緒
- Java多執行緒面試高配問題---多執行緒(3)🧵Java執行緒面試