java 并发编程学习笔记(五)之 不可变对象、同步容器、juc并发容器

举报
小米粒-biubiubiu 发表于 2020/12/02 23:34:43 2020/12/02
【摘要】                       不可变对象、同步容器、juc并发容器 (1)不可变对象: 不可变对象需要满足的条件对象创建以后其状态就不能修改对象所有域都是final类型对象时正确创建(在对象创建期间,this引用没有溢出) final 关键字:类、方法、变量 修饰类:不能被继承 修饰方法:锁定方法不能被继承类修改 ,效率 修饰变量:基本数据类型变量,引...

                      不可变对象、同步容器、juc并发容器

(1)不可变对象:

  • 不可变对象需要满足的条件
  • 对象创建以后其状态就不能修改
  • 对象所有域都是final类型
  • 对象时正确创建(在对象创建期间,this引用没有溢出)

final 关键字:类、方法、变量

修饰类:不能被继承

修饰方法:锁定方法不能被继承类修改 ,效率

修饰变量:基本数据类型变量,引用类型变量


      @Slf4j
      public class ImmutableExample1 {
      private final static Integer a = 1;
      private final static String b = "2";
      private final static Map<Integer, Integer> map1 = Maps.newHashMap();
      static {
       map1.put(1, 2);
       map1.put(3, 4);
       map1.put(5, 6);
       }
      private static Map<Integer, Integer> map2 = Maps.newHashMap();
      private static List<Integer> 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<Integer> list3 = ImmutableList.of(11, 2, 3, 6);
      private static final List<Integer> list4 = new ArrayList<Integer>();
      private static final ImmutableMap<Integer, Integer> unMap = ImmutableMap.of(1, 2, 3, 4);
      private static final ImmutableMap<Integer, Integer> unMap1 = ImmutableMap.<Integer, Integer>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<Long> requestHolder = new ThreadLocal<Long>();
      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 {
      //请求次数
      private static int clientTotal = 5000;
      //允许同时运行的线程数
      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 {
      //请求次数
      private static int clientTotal = 5000;
      //允许同时运行的线程数
      private static int threadTotal = 200;
      private static List<Integer> list = new ArrayList<Integer>();
      private static Vector<Integer> vector =new Vector<>();
      static {
       vector.add(1);
       vector.add(2);
       vector.add(3);
       }
      private  static  List<Integer> safeList  = Collections.synchronizedList(new ArrayList<Integer>());
      private static Set<Integer> set =new HashSet<Integer>();
      private  static  Set<Integer> safeSet  = Collections.synchronizedSet(new HashSet<Integer>());
      private static  Map<Integer,Integer> map = new HashMap<Integer, Integer>();
      private static Map<Integer,Integer> safeMap = new Hashtable<>();
      private  static  Map<Integer,Integer> safeMap1  = Collections.synchronizedMap(new HashMap<Integer,Integer>());
      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循环删除时报错",e);
       }
      try {
      Iterator<Integer> iterator = vector.iterator();
      while(iterator.hasNext()){ //推荐
      if(iterator.next() == 3){
      iterator.remove();
       }
       }
       }catch (Exception e){
       log.error("iterator循环删除时报错",e);
       }
      try {
      for (int i =0; i< vector.size();i++) {  //推荐
      if(i == 3){
       vector.remove(i);
       }
       }
       }catch (Exception e){
       log.error("for循环删除时报错",e);
       }
       }
      }
  
 

(5)并发容器  

  •  CopyOnWriteArrayList

CopyOnWriteArrayList是ArrayList的一个线程安全的变体,其中所有可变操作(add、set等等)都是通过对底层数组进行一次新的复制来实现的。与ArrayList不同处就在于是否会拷贝数组和加锁.

CopyOnWriteArrayList顾名思义就是写时复制的ArrayList,其意思就是在修改容器的元素时,并不是直接在原数组上修改,而是先拷贝了一份数组,然后在拷贝的数组上进行修改,修改完后将其引用赋值给原数组的引用。这样体现了读写分离,这样无论在任何时候我们都可以对容器进行读取。

所谓动态数组操作机制:即通过volatile修饰的Object类型数组来进行数组的CRUD操作。在进行add,set,remove等可变操作的时候,都会先新建一个数组把更新的值赋给该数组,然后再传递给上面的array数组来保持该次操作的可见性。这也是CopyOnWriteArrayList命名的由来。这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,即在进行读操作时的效率要远远高于写或是修改操作,这种方法可能比其他替代方法更 有效。

  • CopyOnWriteArraySet

它是线程安全的无序的集合,可以将它理解成线程安全的HashSet。有意思的是,CopyOnWriteArraySet和HashSet虽然都继承于共同的父类AbstractSet;但是,HashSet是通过“散列表(HashMap)”实现的,而CopyOnWriteArraySet则是通过“动态数组(CopyOnWriteArrayList)”实现的,并不是散列表。
和CopyOnWriteArrayList类似,CopyOnWriteArraySet具有以下特性:
1. 它最适合于具有以下特征的应用程序:Set 大小通常保持很小,只读操作远多于可变操作,需要在遍历期间防止线程间的冲突。
2. 它是线程安全的。
3. 因为通常需要复制整个基础数组,所以可变操作(add()、set() 和 remove() 等等)的开销很大。
4. 迭代器支持hasNext(), next()等不可变操作,但不支持可变 remove()等 操作。
5. 使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。

  •  ConcurrentSkipListSet

ConcurrentSkipListSet是线程安全的有序的集合,适用于高并发的场景。
ConcurrentSkipListSet和TreeSet,它们虽然都是有序的集合。但是,第一,它们的线程安全机制不同,TreeSet是非线程安全的,而ConcurrentSkipListSet是线程安全的。第二,ConcurrentSkipListSet是通过ConcurrentSkipListMap实现的,而TreeSet是通过TreeMap实现的。

  • ConcurrentHashMap

ConcurrentHashMap是线程安全且高效的HashMap。正常业务场景中,我们会经常会用到HashMap,而在多线程环境下,java.util.Hashmap进行put操作时会导致死循环,是因为多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会死循环获取Entry。HashMap put时,发生死循环的原因是因为rehash时导致

而线程安全的HashTable 使用synchronized来保证线程安全,在线程锁竞争激烈的情况下 HashTable的效率非常低下。在Hashtable里,同一把锁连get都会使用synchronized来保证线程安全,Hashtable会竞争同一把锁,所以效率低下。若是能够变成多把锁,就能有效提升并发的效率。ConcurrentHashMap采用了锁分段技术,并且设计与实现非常精巧,大量的利用了volatile,final,CAS等lock-free技术来减少锁竞争对于性能的影响

  • ConcurrentSkipListMap

ConcurrentSkipListMap提供了一种线程安全的并发访问的排序映射表。内部是SkipList(跳表)结构实现,在理论上能够在O(log(n))时间内完成查找、插入、删除操作。
         SkipList是一种红黑树的替代方案,由于SkipList与红黑树相比无论从理论和实现都简单许多,所以得到了很好的推广。SkipList是基于一种统计学原理实现的,有可能出现最坏情况,即查找和更新操作都是O(n)时间复杂度,但从统计学角度分析这种概率极小。
使用SkipList类型的数据结构更容易控制多线程对集合访问的处理,因为链表的局部处理性比较好,当多个线程对SkipList进行更新操作(指插入和删除)时,SkipList具有较好的局部性,每个单独的操作,对整体数据结构影响较小。而如果使用红黑树,很可能一个更新操作,将会波及整个树的结构,其局部性较差。因此使用SkipList更适合实现多个线程的并发处理。
         在非多线程的情况下,应当尽量使用TreeMap。此外对于并发性相对较低的并行程序可以使用Collections.synchronizedSortedMap将TreeMap进行包装,也可以提供较好的效率。对于高并发程序,应当使用ConcurrentSkipListMap,能够提供更高的并发度。
      所以在多线程程序中,如果需要对Map的键值进行排序时,请尽量使用ConcurrentSkipListMap,可能得到更好的并发度。
      注意,调用ConcurrentSkipListMap的size时,由于多个线程可以同时对映射表进行操作,所以映射表需要遍历整个链表才能返回元素个数,这个操作是个O(log(n))的操作。

在JDK1.8中,ConcurrentHashMap的性能和存储空间要优于ConcurrentSkipListMap,但是ConcurrentSkipListMap有一个功能: 它会按照键的自然顺序进行排序。

 总结:

线程限制 : 一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改

共享只读:一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问,但是

任何线程都不能修改它

线程安全对象: 一个线程安全的对象或者容器,在内部通过同步机制保证线程安全,所以其他线程

无需额外的同步就可以通过公共接口随意访问它

被守护对象:被守护对象只能通过获取特定的锁来访问

 

文章来源: blog.csdn.net,作者:血煞风雨城2018,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/qq_31905135/article/details/84248800

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。