java 并發(fā)編程學(xué)習(xí)筆記(五)之 不可變對象、同步容器、juc并發(fā)容器

      網(wǎng)友投稿 727 2025-03-31

      不可變對象、同步容器、juc并發(fā)容器


      (1)不可變對象:

      不可變對象需要滿足的條件

      對象創(chuàng)建以后其狀態(tài)就不能修改

      對象所有域都是final類型

      對象時正確創(chuàng)建(在對象創(chuàng)建期間,this引用沒有溢出)

      final 關(guān)鍵字:類、方法、變量

      修飾類:不能被繼承

      修飾方法:鎖定方法不能被繼承類修改 ,效率

      修飾變量:基本數(shù)據(jù)類型變量,引用類型變量

      @Slf4j

      public class ImmutableExample1 {

      private final static Integer a = 1;

      private final static String b = "2";

      private final static Map map1 = Maps.newHashMap();

      static {

      map1.put(1, 2);

      map1.put(3, 4);

      map1.put(5, 6);

      }

      private static Map map2 = Maps.newHashMap();

      private static List list2 = new ArrayList<>();

      static {

      map2.put(1, 2);

      map2.put(3, 4);

      map2.put(5, 6);

      //通過Collections獲取一個不可被修改的map

      map2 = Collections.unmodifiableMap(map2);

      list2.add(1);

      list2.add(2);

      //通過Collections獲取一個不可被修改的list

      list2 = Collections.unmodifiableList(list2);

      }

      //通過guava 也可以獲取不可變list,set ,map

      private static final ImmutableList list3 = ImmutableList.of(11, 2, 3, 6);

      private static final List list4 = new ArrayList();

      private static final ImmutableMap unMap = ImmutableMap.of(1, 2, 3, 4);

      private static final ImmutableMap unMap1 = ImmutableMap.builder().put(1, 6).build();

      static {

      list4.add(1);

      list4.add(2);

      }

      public static void main(String[] args) {

      // a = 2 ;

      // b ="3";

      // map =Maps.newHashMap();

      map1.put(1, 3);

      log.info("{}", map1.get(1));

      map2.put(1, 3);

      list2.add(4);

      log.info("{}", map2.get(1));

      list3.add(56);

      ImmutableSet list2 = ImmutableSet.copyOf(list4);

      list2.add(7);

      unMap.put(1, 6);

      unMap1.put(1, 8);

      }

      }

      (2) ? 線程 封閉

      public class RequestHolder {

      private final static ThreadLocal requestHolder = new ThreadLocal();

      public static void add(Long id) {

      requestHolder.set(id);

      }

      public static Long getId() {

      return requestHolder.get();

      }

      public static void remove() {

      requestHolder.remove();

      }

      }

      (3)常見的線程不安全的類

      public class StringExample1 {

      //請求次數(shù)

      private static int clientTotal = 5000;

      //允許同時運(yùn)行的線程數(shù)

      private static int threadTotal = 200;

      /**

      * stringBuilder 線程不安全

      * stringBuffer 線程安全

      */

      //public static StringBuilder stringBuilder=new StringBuilder();

      public static StringBuffer stringBuffer = new StringBuffer();

      /**

      * simpleDateFormat 不是線程安全的

      * joda-time 的dateTimeFormatter 是線程安全的

      */

      public static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd");

      public static DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy/MM/dd");

      public static void main(String[] args) {

      ExecutorService executorService = Executors.newCachedThreadPool();

      final Semaphore semaphore = new Semaphore(threadTotal);

      final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);

      for (int i = 0; i < clientTotal; i++) {

      executorService.execute(() -> {

      try {

      semaphore.acquire();

      append();

      semaphore.release();

      } catch (InterruptedException e) {

      log.error("exception", e);

      }

      countDownLatch.countDown();

      });

      }

      try {

      countDownLatch.await();

      } catch (InterruptedException e) {

      e.printStackTrace();

      }

      executorService.shutdown();

      log.info("count:{}", stringBuffer.length());

      }

      private static void append() {

      DateTime dateTime = DateTime.parse("2018/06/07", dateTimeFormatter);

      log.info(dateTime.toDate().toString());

      }

      }

      (4)同步容器

      @Slf4j

      public class ContainExample {

      //請求次數(shù)

      private static int clientTotal = 5000;

      //允許同時運(yùn)行的線程數(shù)

      private static int threadTotal = 200;

      private static List list = new ArrayList();

      private static Vector vector =new Vector<>();

      static {

      vector.add(1);

      vector.add(2);

      vector.add(3);

      }

      private static List safeList = Collections.synchronizedList(new ArrayList());

      private static Set set =new HashSet();

      private static Set safeSet = Collections.synchronizedSet(new HashSet());

      private static Map map = new HashMap();

      private static Map safeMap = new Hashtable<>();

      private static Map safeMap1 = Collections.synchronizedMap(new HashMap());

      java 并發(fā)編程學(xué)習(xí)筆記(五)之 不可變對象、同步容器、juc并發(fā)容器

      public static void main(String[] args) {

      ExecutorService executorService = Executors.newCachedThreadPool();

      final Semaphore semaphore = new Semaphore(threadTotal);

      final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);

      for (int i = 0; i < clientTotal; i++) {

      final int index = i;

      executorService.execute(() -> {

      try {

      semaphore.acquire();

      putValue(index);

      semaphore.release();

      } catch (InterruptedException e) {

      log.error("exception", e);

      }

      countDownLatch.countDown();

      });

      }

      try {

      countDownLatch.await();

      } catch (InterruptedException e) {

      e.printStackTrace();

      }

      executorService.shutdown();

      log.info("count:{}", safeMap.size());

      // testVector();

      testVector1();

      }

      private static void putValue(int i) {

      safeMap.put(i,i);

      }

      public static void testVector(){

      //ExecutorService executorService = Executors.newCachedThreadPool();

      //在這種情況下 線程的 vector 也會變得線程不安全

      while(true) {

      new Thread(() -> {

      for (int i = 0, l = vector.size(); i < l; i++) {

      vector.remove(i);

      }

      }).start();

      new Thread(() -> {

      for (int i = 0, l = vector.size(); i < l; i++) {

      vector.get(i);

      }

      }).start();

      }

      }

      public static void testVector1(){

      try {

      for (Integer i : vector) { //不推薦

      if(i == 3){

      vector.remove(i);

      }

      }

      }catch (Exception e){

      log.error("foreach循環(huán)刪除時報錯",e);

      }

      try {

      Iterator iterator = vector.iterator();

      while(iterator.hasNext()){ //推薦

      if(iterator.next() == 3){

      iterator.remove();

      }

      }

      }catch (Exception e){

      log.error("iterator循環(huán)刪除時報錯",e);

      }

      try {

      for (int i =0; i< vector.size();i++) { //推薦

      if(i == 3){

      vector.remove(i);

      }

      }

      }catch (Exception e){

      log.error("for循環(huán)刪除時報錯",e);

      }

      }

      }

      (5)并發(fā)容器

      CopyOnWriteArrayList

      CopyOnWriteArrayList是ArrayList的一個線程安全的變體,其中所有可變操作(add、set等等)都是通過對底層數(shù)組進(jìn)行一次新的復(fù)制來實(shí)現(xiàn)的。與ArrayList不同處就在于是否會拷貝數(shù)組和加鎖.

      CopyOnWriteArrayList顧名思義就是寫時復(fù)制的ArrayList,其意思就是在修改容器的元素時,并不是直接在原數(shù)組上修改,而是先拷貝了一份數(shù)組,然后在拷貝的數(shù)組上進(jìn)行修改,修改完后將其引用賦值給原數(shù)組的引用。這樣體現(xiàn)了讀寫分離,這樣無論在任何時候我們都可以對容器進(jìn)行讀取。

      所謂動態(tài)數(shù)組操作機(jī)制:即通過volatile修飾的Object類型數(shù)組來進(jìn)行數(shù)組的CRUD操作。在進(jìn)行add,set,remove等可變操作的時候,都會先新建一個數(shù)組把更新的值賦給該數(shù)組,然后再傳遞給上面的array數(shù)組來保持該次操作的可見性。這也是CopyOnWriteArrayList命名的由來。這一般需要很大的開銷,但是當(dāng)遍歷操作的數(shù)量大大超過可變操作的數(shù)量時,即在進(jìn)行讀操作時的效率要遠(yuǎn)遠(yuǎn)高于寫或是修改操作,這種方法可能比其他替代方法更 有效。

      CopyOnWriteArraySet

      它是線程安全的無序的集合,可以將它理解成線程安全的HashSet。有意思的是,CopyOnWriteArraySet和HashSet雖然都繼承于共同的父類AbstractSet;但是,HashSet是通過“散列表(HashMap)”實(shí)現(xiàn)的,而CopyOnWriteArraySet則是通過“動態(tài)數(shù)組(CopyOnWriteArrayList)”實(shí)現(xiàn)的,并不是散列表。

      和CopyOnWriteArrayList類似,CopyOnWriteArraySet具有以下特性:

      1. 它最適合于具有以下特征的應(yīng)用程序:Set 大小通常保持很小,只讀操作遠(yuǎn)多于可變操作,需要在遍歷期間防止線程間的沖突。

      2. 它是線程安全的。

      3. 因為通常需要復(fù)制整個基礎(chǔ)數(shù)組,所以可變操作(add()、set() 和 remove() 等等)的開銷很大。

      4. 迭代器支持hasNext(), next()等不可變操作,但不支持可變 remove()等 操作。

      5. 使用迭代器進(jìn)行遍歷的速度很快,并且不會與其他線程發(fā)生沖突。在構(gòu)造迭代器時,迭代器依賴于不變的數(shù)組快照。

      ConcurrentSkipListSet

      ConcurrentSkipListSet是線程安全的有序的集合,適用于高并發(fā)的場景。

      ConcurrentSkipListSet和TreeSet,它們雖然都是有序的集合。但是,第一,它們的線程安全機(jī)制不同,TreeSet是非線程安全的,而ConcurrentSkipListSet是線程安全的。第二,ConcurrentSkipListSet是通過ConcurrentSkipListMap實(shí)現(xiàn)的,而TreeSet是通過TreeMap實(shí)現(xiàn)的。

      ConcurrentHashMap

      ConcurrentHashMap是線程安全且高效的HashMap。正常業(yè)務(wù)場景中,我們會經(jīng)常會用到HashMap,而在多線程環(huán)境下,Java.util.Hashmap進(jìn)行put操作時會導(dǎo)致死循環(huán),是因為多線程會導(dǎo)致HashMap的Entry鏈表形成環(huán)形數(shù)據(jù)結(jié)構(gòu),一旦形成環(huán)形數(shù)據(jù)結(jié)構(gòu),Entry的next節(jié)點(diǎn)永遠(yuǎn)不為空,就會死循環(huán)獲取Entry。HashMap put時,發(fā)生死循環(huán)的原因是因為rehash時導(dǎo)致

      而線程安全的HashTable 使用synchronized來保證線程安全,在線程鎖競爭激烈的情況下 HashTable的效率非常低下。在Hashtable里,同一把鎖連get都會使用synchronized來保證線程安全,Hashtable會競爭同一把鎖,所以效率低下。若是能夠變成多把鎖,就能有效提升并發(fā)的效率。ConcurrentHashMap采用了鎖分段技術(shù),并且設(shè)計與實(shí)現(xiàn)非常精巧,大量的利用了volatile,final,CAS等lock-free技術(shù)來減少鎖競爭對于性能的影響

      ConcurrentSkipListMap

      ConcurrentSkipListMap提供了一種線程安全的并發(fā)訪問的排序映射表。內(nèi)部是SkipList(跳表)結(jié)構(gòu)實(shí)現(xiàn),在理論上能夠在O(log(n))時間內(nèi)完成查找、插入、刪除操作。

      SkipList是一種紅黑樹的替代方案,由于SkipList與紅黑樹相比無論從理論和實(shí)現(xiàn)都簡單許多,所以得到了很好的推廣。SkipList是基于一種統(tǒng)計學(xué)原理實(shí)現(xiàn)的,有可能出現(xiàn)最壞情況,即查找和更新操作都是O(n)時間復(fù)雜度,但從統(tǒng)計學(xué)角度分析這種概率極小。

      使用SkipList類型的數(shù)據(jù)結(jié)構(gòu)更容易控制多線程對集合訪問的處理,因為鏈表的局部處理性比較好,當(dāng)多個線程對SkipList進(jìn)行更新操作(指插入和刪除)時,SkipList具有較好的局部性,每個單獨(dú)的操作,對整體數(shù)據(jù)結(jié)構(gòu)影響較小。而如果使用紅黑樹,很可能一個更新操作,將會波及整個樹的結(jié)構(gòu),其局部性較差。因此使用SkipList更適合實(shí)現(xiàn)多個線程的并發(fā)處理。

      在非多線程的情況下,應(yīng)當(dāng)盡量使用TreeMap。此外對于并發(fā)性相對較低的并行程序可以使用Collections.synchronizedSortedMap將TreeMap進(jìn)行包裝,也可以提供較好的效率。對于高并發(fā)程序,應(yīng)當(dāng)使用ConcurrentSkipListMap,能夠提供更高的并發(fā)度。

      所以在多線程程序中,如果需要對Map的鍵值進(jìn)行排序時,請盡量使用ConcurrentSkipListMap,可能得到更好的并發(fā)度。

      注意,調(diào)用ConcurrentSkipListMap的size時,由于多個線程可以同時對映射表進(jìn)行操作,所以映射表需要遍歷整個鏈表才能返回元素個數(shù),這個操作是個O(log(n))的操作。

      在JDK1.8中,ConcurrentHashMap的性能和存儲空間要優(yōu)于ConcurrentSkipListMap,但是ConcurrentSkipListMap有一個功能:?它會按照鍵的自然順序進(jìn)行排序。

      總結(jié):

      線程限制 : 一個被線程限制的對象,由線程獨(dú)占,并且只能被占有它的線程修改

      共享只讀:一個共享只讀的對象,在沒有額外同步的情況下,可以被多個線程并發(fā)訪問,但是

      任何線程都不能修改它

      線程安全對象: 一個線程安全的對象或者容器,在內(nèi)部通過同步機(jī)制保證線程安全,所以其他線程

      無需額外的同步就可以通過公共接口隨意訪問它

      被守護(hù)對象:被守護(hù)對象只能通過獲取特定的鎖來訪問

      Java 任務(wù)調(diào)度 容器 數(shù)據(jù)結(jié)構(gòu)

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:企業(yè)管理獎懲辦法,提高績效的最佳實(shí)踐
      下一篇:如何將wps表格保存為圖片(wps表格怎么存成圖片)
      相關(guān)文章
      亚洲精品无码久久久久APP| 亚洲成aⅴ人在线观看| 激情综合亚洲色婷婷五月| 亚洲人成网站影音先锋播放| 亚洲综合AV在线在线播放| 久久亚洲欧美国产精品| 亚洲人成网站在线观看播放动漫 | 亚洲第一黄片大全| 日日摸日日碰夜夜爽亚洲| 精品国产日韩亚洲一区91| 日本亚洲中午字幕乱码| 亚洲国产精品丝袜在线观看| 亚洲A∨午夜成人片精品网站| gogo全球高清大胆亚洲| 亚洲国产成人久久一区WWW| 亚洲欧洲精品成人久久曰影片| 亚洲人成网站观看在线播放| 国产亚洲精品不卡在线| 久久影院亚洲一区| 国精无码欧精品亚洲一区| 亚洲AV无码成人精品区天堂 | www国产亚洲精品久久久日本| 亚洲成av人在片观看| 国产国拍亚洲精品福利| 伊人久久大香线蕉亚洲五月天 | 亚洲av最新在线观看网址| 色婷婷亚洲一区二区三区| 亚洲福利精品电影在线观看| 国产专区一va亚洲v天堂| 国产亚洲综合久久系列| 久久久久亚洲AV片无码| 亚洲精品韩国美女在线| 国产成人精品亚洲2020| 亚洲国产AV一区二区三区四区| 日韩精品亚洲专区在线观看| 狠狠综合久久综合88亚洲| 亚洲AV日韩精品久久久久久久| 亚洲美女视频网址| 亚洲综合成人婷婷五月网址| 色天使色婷婷在线影院亚洲| 狠狠亚洲婷婷综合色香五月排名|