parallelStream中的執行緒安全問題

不一樣的花朵發表於2018-12-14

parallelStream中的執行緒安全問題


在面試的時候很多人喜歡問併發程式設計,那麼在實際開發中我們能用到多少呢?今天在這裡舉個例子就是實際開發中的併發程式設計的問題。在我們經常寫的業務程式碼中很多時候會出現遍歷迴圈的情況,比如取集合資料、封裝集合資料等等,這是我們不能避免的。
在jdk1.8中給我們提供了stream;為什麼在很多時候我們的遍歷還是進行普通的迴圈?因為這個和我們的程式設計習慣有關係,我最初接觸的就是普通的迴圈,而且一用很多年,所以有時候在業務很緊急的情況下,首先碼出來的肯定是我們千錘百煉的手法。這不是我們的錯,錯就錯在了誰讓它出的這麼晚了?如果從一開始就用的是stream,那你用起來肯定比別人快很多。在這裡不是教大家怎麼使用流,這個百度一大堆。在這裡說的是使用並行會出現很多問題。言歸正傳!
在很多時候普通的for迴圈以後夠了,因為資料量不大的情況下,jdk底層對它的優化是非常好的。所以看情況而定,不是說所有的迴圈都要用流遍歷。大資料量的遍歷用parallelStream可以比普通遍歷節省一半的時間,這個親測過。
在使用stream.foreach時這個遍歷沒有執行緒安全問題,但是使用parallelStream就會有執行緒安全問題,所有在parallelStream裡面使用的外部變數,比如集合一定要使用執行緒安全集合,不然就會引發多執行緒安全問題。在並行時,實際上是多個執行緒執行,這個時候還有個問題,就是當你在遍歷中使用例如請求裡面的資料時,就會報一個異常,這個異常就是多個執行緒執行,但是其他執行緒沒有這個請求的資料,所以獲取不到。這時解決辦法是把需要的資料在遍歷外面取到,再傳遞進去就可以解決。
在這裡順帶說一下排序,儘量不要自己去實現排序,這個效能並不理想。儘量用jdk自己的排序,底層對jdk排序優化,不是我們所能比擬的。這個也是親測過的。
例如 :
// 這是假設的資料

Map<Integer, Object> dataMap = Maps.newConcurrentMap();
Map<Integer, Object> error = Maps.newConcurrentMap();
Map<Integer, Object> rest = new TreeMap<Integer, Object>(new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
// 假設這是一個需要從請求中獲取的資料,所以先從外面獲取,再傳遞進去
String number = request.getParameter(‘number’);
// 假設封裝了一個集合資料
List listStudent = new ArrayList<>();
listStudent.parallelStream().forEach(student -> {
// 在遍歷時取索引
int index = listStudent.indexOf(student);
// 校驗資料
handleData(index, student, number, error, dataMap);
});

相關文章