暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

JUC(02)

爱德华的奇妙生活 2020-08-08
132

集合类不安全

List不安全

    package com.zhang.unsafe;


    import java.util.*;
    import java.util.concurrent.CopyOnWriteArrayList;


    //并发修改异常
    public class ListTest {
    public static void main(String[] args) {
    //并发下ArrayList是不安全的
    /**
    * 解决方案:
    * 1. List<String> list = new Vector<>(); Vector默认就是安全的,出现于1.0 ArrayList默认是非安全的,出现于1.8
    *
    * 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
    *
    * 3. List<String> list = new CopyOnWriteArrayList<>(); JUC下的
    * */


    // CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略
    //多个线程调用的时候,list,读取的时候是固定的,写入的时候不能让其同时在上面去写,会造成覆盖
    //写入的时候避免覆盖,造成数据问题
    //CopyOnWriteArrayList 比 Vector 优于哪里?Vector使用的是Synchronized,CopyOnWriteArrayList使用lock锁,性能高于Vector
    List<String> list = new CopyOnWriteArrayList<>();
    for (int i = 0; i < 10; i++) {
    new Thread(()->{
    list.add(UUID.randomUUID().toString().substring(0,5));
    System.out.println(list);
    },String.valueOf(i)).start();
    }
    }
    }

    set不安全

      package com.zhang.unsafe;


      import java.util.Collections;
      import java.util.HashSet;
      import java.util.Set;
      import java.util.UUID;
      import java.util.concurrent.CopyOnWriteArraySet;


      /*
      * 同理可证 java.util.ConcurrentModificationException
      *
      * 解决方案 :1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
      *
      * 2.Set<String> set = new CopyOnWriteArraySet<>();
      * */
      public class SetTest {
      public static void main(String[] args) {
      //Set<String> set = new HashSet<>();
      //Set<String> set = Collections.synchronizedSet(new HashSet<>());
      Set<String> set = new CopyOnWriteArraySet<>();
      for (int i = 0; i < 30; i++) {
      new Thread(()->{
      set.add(UUID.randomUUID().toString().substring(0,5));
      System.out.println(set);
      },String.valueOf(i)).start();
      }
      }
      }
      HashSet底层是什么?就是hashmap,HashSet里面仅仅是new了一个HashMap<>();
        public HashSet() {
        map = new HashMap<>();
        }


        //add set的本质就是map的key,因为key是不可以重复的
        public boolean add(E e) {
        return map.put(e, PRESENT)==null;
        }
        //PRESENT 是一个常量,固定的值
        private static final Object PRESENT = new Object();

        Map不安全

          package com.zhang.unsafe;


          import java.util.HashMap;
          import java.util.Map;
          import java.util.UUID;
          import java.util.concurrent.ConcurrentHashMap;


          //ConcurrentModificationException
          public class MapTest {


          public static void main(String[] args) {


          //map是这样使用的吗?不是,工作中不用HashMap
          //默认等价什么?new HashMap<>(16,0.75f)
          //Map<String, Object> map = new HashMap<>();
          //负载因子(默认是0.75)、初始化容量(1<<4 16)
          Map<String, String> map = new ConcurrentHashMap<>();


          for (int i = 0; i <=30; i++) {
          new Thread(()->{
          map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
          System.out.println(map);
          }).start();
          }
          }
          }

          Callable

          1. 可以有返回值

          2. 可以抛出异常

          3. 方法不同,run()/call()

            package com.zhang.callable;


            import java.util.concurrent.Callable;
            import java.util.concurrent.ExecutionException;
            import java.util.concurrent.FutureTask;


            public class CallableTest {
            public static void main(String[] args) throws ExecutionException, InterruptedException {
            //1. 传统方法
            //new Thread(new MyThread()).start();




            //new Thread(new FutureTask<V>()).start();
            //new Thread(new FutureTask<V>(Callable)).start();
            //new Thread().start();//怎么启动Callable


            MyThread thread = new MyThread();
            //适配类
            FutureTask futureTask = new FutureTask(thread);//thread的返回值会被futureTask接受


            new Thread(futureTask,"A").start();
            new Thread(futureTask,"B").start();//只会输出一个call(),结果会被缓存,效率高
            String o = (String) futureTask.get();//获取Callable的返回结果;这个get方法可能会产生阻塞,把它放到最后,或者使用异步通信来处理
            System.out.println(o);
            }
            }


            //泛型的参数等于方法的返回值
            class MyThread implements Callable<String>{


            @Override
            public String call() throws Exception {
            System.out.println("call()");
            //耗时的操作
            return "123";
            }
            }
            //1. 传统方式
            /*
            class MyThread implements Runnable{


            @Override
            public void run() {


            }
            }*/

            常用辅助类

            CountDownLatch

              package com.zhang.add;


              import java.util.concurrent.CountDownLatch;


              //计数器(减法)
              public class DountDownLatchDemo {
              public static void main(String[] args) throws InterruptedException {
              //总数是6,用于倒计时,
              CountDownLatch countDownLatch = new CountDownLatch(6);
              for (int i = 0; i < 6; i++) {
              new Thread(()->{
              System.out.println(Thread.currentThread().getName()+"go out");
              countDownLatch.countDown();//-1
              },String.valueOf(i)).start();
              }

              countDownLatch.await();//等待计数器归零,然后再向下执行,用于必须要执行的任务时


              System.out.println("Close door");
              //countDownLatch.countDown();//-1
              }
              }

              原理:

              countDownLatch.countDown(); -1

              countDownLatch.await();//等待计数器归零,然后再向下执行

              每次有线程调用countDown()数量-1,假设计数器变为0,countDownLatch.await()就会被唤醒,继续执行。

              CycliBarrier

              加法计数器
                package com.zhang.add;


                import java.util.concurrent.BrokenBarrierException;
                import java.util.concurrent.CyclicBarrier;


                public class CyclicBarrierDemo {
                public static void main(String[] args) {
                CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
                System.out.println("主线程启动");
                });


                for (int i = 0; i < 7; i++) {
                final int temp = i;//lambda不能操作i,需要通过中间变量
                new Thread(()->{
                System.out.println(Thread.currentThread().getName()+temp+"个副线程");


                try {
                cyclicBarrier.await();//等待
                } catch (InterruptedException e) {
                e.printStackTrace();
                } catch (BrokenBarrierException e) {
                e.printStackTrace();
                }
                }).start();
                }
                }
                }
                注意:如果副线程数始终无法到达7,那么线程将会一直停留在子线程部分。

                Semaphore

                  package com.zhang.add;


                  import java.util.concurrent.Semaphore;
                  import java.util.concurrent.TimeUnit;


                  public class SemaphoreDemo {
                  public static void main(String[] args) {
                  //限流
                  //线程数量:停车位
                  Semaphore semaphore = new Semaphore(3);


                  for (int i = 0; i <= 6; i++) {
                  new Thread(()->{


                  try {
                  semaphore.acquire();//acquire(),得到
                  System.out.println(Thread.currentThread().getName()+"抢到车位");
                  TimeUnit.SECONDS.sleep(3);//停留时间
                  System.out.println(Thread.currentThread().getName()+"离开车位");
                  } catch (InterruptedException e) {
                  e.printStackTrace();
                  }finally {
                  semaphore.release();//release()释放
                  }


                  },String.valueOf(i)).start();
                  }
                  }
                  }

                  原理:

                  semaphore.acquire();获得,假设如果已经满了,等待被释放为止

                  semaphore.release() 释放,会将当前的信号量释放,然后唤醒等待的线程

                  作用:多个共享资源互斥的使用!并发限流,控制最大的线程数

                  读写锁( ReadWriteLock)

                    package com.zhang.rw;


                    import java.util.HashMap;
                    import java.util.Map;
                    import java.util.concurrent.locks.ReadWriteLock;
                    import java.util.concurrent.locks.ReentrantReadWriteLock;


                    /**
                    * 独占锁(写锁) 一次只能被一个线程占有
                    * 共享锁(写锁) 多个线程可以同时占有
                    * ReadWriteLock
                    * 读-读 可以共存
                    * 读-写 不能共存
                    * 写-写 不能共存
                    */
                    public class ReadWriteLockDemo {
                    public static void main(String[] args) {


                    //MyCache myCache = new MyCache();
                    MyCacheLock myCache = new MyCacheLock();


                    //写入
                    for (int i = 1; i <= 5; i++) {
                    final int temp = i;
                    new Thread(()->{
                    myCache.put(temp+"",temp+"");
                    },String.valueOf(i)).start();
                    }


                    //读取
                    for (int i = 1; i <= 5; i++) {
                    final int temp = i;
                    new Thread(()->{
                    myCache.get(temp+"");
                    },String.valueOf(i)).start();
                    }


                    }
                    }


                    //加锁的
                    class MyCacheLock{
                    private volatile Map<String,Object> map = new HashMap<>();
                    //读写锁:更加细粒度的控制
                    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();


                    //存 ,写,只希望同时只有一个线程写
                    public void put(String key,Object value){
                    readWriteLock.writeLock().lock();
                    try {
                    System.out.println(Thread.currentThread().getName()+"写入"+key);
                    map.put(key,value);
                    System.out.println(Thread.currentThread().getName()+"写入OK");
                    } catch (Exception e) {
                    e.printStackTrace();
                    }finally {
                    readWriteLock.writeLock().unlock();
                    }


                    }


                    //取,读
                    public void get(String key){
                    readWriteLock.readLock().lock();


                    try {
                    System.out.println(Thread.currentThread().getName()+"读取"+key);
                    Object o = map.get(key);
                    System.out.println(Thread.currentThread().getName()+"读取OK"+key);
                    } catch (Exception e) {
                    e.printStackTrace();
                    } finally {
                    readWriteLock.readLock().unlock();
                    }
                    }
                    }


                    /*
                    自定义缓存 set get


                    */
                    class MyCache{
                    private volatile Map<String,Object> map = new HashMap<>();


                    //存 ,写
                    public void put(String key,Object value){
                    System.out.println(Thread.currentThread().getName()+"写入"+key);
                    map.put(key,value);
                    System.out.println(Thread.currentThread().getName()+"写入OK"+key);
                    }


                    //取,读
                    public void get(String key){
                    System.out.println(Thread.currentThread().getName()+"读取"+key);
                    Object o = map.get(key);
                    System.out.println(Thread.currentThread().getName()+"读取OK"+key);
                    }
                    }

                    文章转载自爱德华的奇妙生活,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                    评论