JUC并发编程学习笔记(简单易懂)2

简介: JUC并发编程学习笔记(简单易懂)

2:不安全的集合类

2.1 常见的不安全集合类:ArrayList

/**
 * .ConcurrentModificationException  并发修改异常
 */
public class test {
   public static void main(String[] args) {
      //并发情况下 ArrayList是不安全的
      List<String> list = new ArrayList<>();
      //Vector<String> list = new Vector<>();
      for (int i = 1; i <= 10 ; i++) {
         new Thread(()->{
            list.add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(list);
         },String.valueOf(i)).start();
      }
   }
}

在并发情况下报错:

Exception in thread "5" java.util.ConcurrentModificationException
  at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:907)
  at java.util.ArrayList$Itr.next(ArrayList.java:857)
  at java.util.AbstractCollection.toString(AbstractCollection.java:461)
  at java.lang.String.valueOf(String.java:2994)
  at java.io.PrintStream.println(PrintStream.java:821)

2.1.1 解决方法:

1:new Vector()

public class test {
   public static void main(String[] args) {
      Vector<String> list = new Vector<>();
      for (int i = 1; i <= 10 ; i++) {
         new Thread(()->{
            list .add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(list );
         },String.valueOf(i)).start();
      }
   }
}

2:Collections.synchronizedList()这个重点

/**
 * .ConcurrentModificationException  并发修改异常
 */
public class test {
   public static void main(String[] args) {
      List<String> list = new ArrayList<>();
      List<String> list2 = Collections.synchronizedList(list);
      for (int i = 1; i <= 10 ; i++) {
         new Thread(()->{
            list2.add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(list2);
         },String.valueOf(i)).start();
      }
   }
}

3:CopyOnWriteArrayList<E>

96fa2b64556b4eb28b24c3d4287f4b74.png

public class test {
   public static void main(String[] args) {
      List<String> list2 = new CopyOnWriteArrayList();
      for (int i = 1; i <= 10 ; i++) {
         new Thread(()->{
            list2.add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(list2);
         },String.valueOf(i)).start();
      }
   }

为啥这个是安全的?(未完善)

/*
         * CopyOnWrite 写入时赋值 COW 计算机程序设计领域的一种优化策略
         * 多个线程调用的时候 , list , 读取的时候 , 固定的, 写入(覆盖)
         * 在写入的时候避免覆盖 , 造成数据问题!
         * 读写分离
        */
1:  
public CopyOnWriteArrayList() {
        setArray(new Object[0]);
}
2:
    final void setArray(Object[] a) {
        array = a;
    }
3:
private transient volatile Object[] array;

为啥不用:Vector 而是选择用 :CopyOnWriteArrayList

1:Vector  被 synchronized  修饰,效率相对低。

1fd50ac6d849441dbe5d444d19d7471f.png

2:CopyOnWriteArrayList  的方法 都没被 synchronized   修饰,效率相对较高。

d8ae78fc0b5c4d8b842b037cb58e48d0.png

2.2 常见的不安全集合类:HashSet()

public class set集合 {
   public static void main(String[] args) {
      Set<String> set = new HashSet<>();
      for (int i = 1; i <= 30 ; i++) {
         new Thread(()->{
            set.add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(set);
         },String.valueOf(i)).start();
      }
   }
}

报错信息:

Exception in thread "11" Exception in thread "16" java.util.ConcurrentModificationException
  at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
  at java.util.HashMap$KeyIterator.next(HashMap.java:1466)
  at java.util.AbstractCollection.toString(AbstractCollection.java:461)
  at java.lang.String.valueOf(String.java:2994)
  at java.io.PrintStream.println(PrintStream.java:821)

2.2.1 解决方法:

1:Collections.synchronizedSet(new HashSet<>());

public class set集合 {
   public static void main(String[] args) {
      //Set<String> seobjectst = new HashSet<>();
      Set<String> set = Collections.synchronizedSet(new HashSet<>());
      for (int i = 1; i <= 30 ; i++) {
         new Thread(()->{
            set.add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(set);
         },String.valueOf(i)).start();
      }
   }
}

1.2  new CopyOnWriteArraySet<>();

public class set集合 {
   public static void main(String[] args) {
      Set<String> set = new CopyOnWriteArraySet<>();
      for (int i = 1; i <= 30 ; i++) {
         new Thread(()->{
            set.add(UUID.randomUUID().toString().substring(0,5));
            System.out.println(set);
         },String.valueOf(i)).start();
      }
   }
}

HashSet的底层就是HashMap

2534ea50761947458d333db9964e5dae.png

7aaff1a61aad4966aa1993e30a810970.png

c7ffd85c76a743e88baccb3130ba17a0.png

map的key不允许重复

2.3 常见的不安全集合类:HashMap()

public class map {
   public static void main(String[] args) {
      Map<String,String> map2 = new HashMap<>();
      for (int i = 0; i < 30; i++) {
         new Thread(()->{
            map2.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
            System.out.println(map2);
            },String.valueOf(i)).start();
      }
   }
}

2.3.1 报错信息

Exception in thread "21" Exception in thread "25" java.util.ConcurrentModificationException
  at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442)
  at java.util.HashMap$EntryIterator.next(HashMap.java:1476)
  at java.util.HashMap$EntryIterator.next(HashMap.java:1474)
  at java.util.AbstractMap.toString(AbstractMap.java:554)
  at java.lang.String.valueOf(String.java:2994)

2.3.2 解决方法

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

3:Callable

c67e2a1fcd1b492b8776a34cca79ca6e.png

0a3b9e4501aa465e96cbec7d18ffeeee.png

3.1 怎么启动Callable?

public class test {
   public static void main(String[] args) throws ExecutionException, InterruptedException {
      TestA test = new TestA();
      FutureTask task = new FutureTask(test); //适配类
      new Thread(task,"A").start();
      new Thread(task,"B").start(); //结果会被缓存,效率高
      /获取callable的返回结果,get方法可能产生阻塞!把他放到最后或者使用异步通信来处理
      String o = String.valueOf(task.get()); //获取callable 的返回结果
      System.out.println(o);
   }
}
class TestA implements Callable<String>{
   @Override
   public String call() throws Exception {
      System.out.println("我是好人");
      return "100";
   }
}
我是好人
100
进程已结束,退出代码0

Runable ---->  实现类:FutureTask--->  Callable

ab180af5bbc1473b9fd52703a272004b.png

4da332f1dffa4f8a8bdad58f75921b32.png

4:常用的辅助类

4.1 CountDownLatch

4.1.1 介绍(减法计数器)

允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。

494cac97ad2c44cca629b29cf7ee379b.png

4.1.2 简单使用

源码:

/**
     * Constructs a {@code CountDownLatch} initialized with the given count.
     *
     * @param count the number of times {@link #countDown} must be invoked
     *        before threads can pass through {@link #await}
     * @throws IllegalArgumentException if {@code count} is negative
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
//计数器
public class CountDownLatchTestA {
   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()+"出去了");
            countDownLatch.countDown();//数量-1
         },String.valueOf(i)).start();
      }
      countDownLatch.await();//等待计数器归零,然后再往下执行
      System.out.println("人都出去完了,我要关门了");
   }
}
打印结果:
0出去了
2出去了
5出去了
1出去了
4出去了
3出去了
人都出去完了,我要关门了

原理:

countDownLatch.countDown();//数量-1

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

每次有线程调用countDown()方法,计数器减1, 假设计算机变为0 ,await()方法就会被唤醒,继续执行!

4.2 CyclicBarrier

4.2.1 介绍(加法计数器)

b05d014a7ec14ab6acab6957f2950af0.png

源码:

//接收一个计数,一个线程参数  
 public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }
public class CyclicBarrierTestA {
   public static void main(String[] args) {
      CyclicBarrier barrier = new CyclicBarrier(7,new Thread(()->{
         System.out.println("成功召唤神龙!!!");
      }));
      for (int i = 1; i <= 7 ; i++) {
         final int temp = i;
         new Thread(()->{
            System.out.println("第"+temp+"颗龙珠收集成功!");
            try {
               barrier.await();
            } catch (InterruptedException e) {
               throw new RuntimeException(e);
            } catch (BrokenBarrierException e) {
               throw new RuntimeException(e);
            }
         }).start();
      }
   }
}
打印:
第1颗龙珠收集成功!
第7颗龙珠收集成功!
第2颗龙珠收集成功!
第3颗龙珠收集成功!
第6颗龙珠收集成功!
第4颗龙珠收集成功!
第5颗龙珠收集成功!
成功召唤神龙!!!

4.3 Semaphore

4.3.1 介绍(信号量)

a3b0844f64e340609ac0b04aea0d5b30.png

源码:

默认是非公平锁,但是可以设置为公平锁

acquire获取资源

release 释放资源

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

 

public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    /**
     * Creates a {@code Semaphore} with the given number of
     * permits and the given fairness setting.
     *
     * @param permits the initial number of permits available.
     *        This value may be negative, in which case releases
     *        must occur before any acquires will be granted.
     * @param fair {@code true} if this semaphore will guarantee
     *        first-in first-out granting of permits under contention,
     *        else {@code false}
     */
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

4.3.2 简单使用

public class tWWW {
   public static void main(String[] args) {
      //线程数量,停车位,限流
      Semaphore semaphore = new Semaphore(3);
      for (int i = 0; i < 6; i++) {
         new Thread(()->{
            try {
               semaphore.acquire();//获得,假设如果满了,等待,等待被释放为止!
               System.out.println(Thread.currentThread().getName()+"抢到车位");
               TimeUnit.SECONDS.sleep(2);
               System.out.println(Thread.currentThread().getName()+"离开车位");
            } catch (InterruptedException e) {
               e.printStackTrace();
            } finally {
               semaphore.release();//释放,会将当前的信号量释放+1,然后唤醒等待的线程!
            }
         },String.valueOf(i)).start();
      }
   }
}
打印结果如下:
2抢到车位
0抢到车位
1抢到车位
2离开车位
0离开车位
1离开车位
4抢到车位
5抢到车位
3抢到车位
4离开车位
5离开车位
3离开车位



目录
相关文章
|
安全 Java
并发编程系列教程(02) - 多线程安全
并发编程系列教程(02) - 多线程安全
30 0
|
存储 缓存 安全
【并发编程的艺术】JAVA并发机制的底层原理
Java代码的执行过程:代码编译->Java字节码->类加载器加载到JVM->JVM执行字节码,最终转化为汇编指令在CPU中执行。所以,Java中使用的并发机制,也依赖于JVM的实现和CPU指令。本章将重点描述这两个关键字的实现,并由此深入探索操作系统底层原理。
75 0
|
3月前
|
安全 数据库连接 API
C#一分钟浅谈:多线程编程入门
在现代软件开发中,多线程编程对于提升程序响应性和执行效率至关重要。本文从基础概念入手,详细探讨了C#中的多线程技术,包括线程创建、管理及常见问题的解决策略,如线程安全、死锁和资源泄露等,并通过具体示例帮助读者理解和应用这些技巧,适合初学者快速掌握C#多线程编程。
84 0
|
Java API 调度
并发编程系列教程(01) - 多线程基础
并发编程系列教程(01) - 多线程基础
73 0
|
7月前
|
安全 Java
多线程(进阶三:JUC)
多线程(进阶三:JUC)
72 0
|
Java 调度
JUC并发编程学习笔记(简单易懂)1
JUC并发编程学习笔记(简单易懂)
55 0
|
Web App开发 安全 Java
JUC高并发编程(一)——JUC基础知识
JUC高并发编程(一)——JUC基础知识
142 0
|
存储 SQL 缓存
JUC 并发编程学习笔记(总)
JUC 并发编程学习笔记(总)
102 0
JUC 并发编程学习笔记(总)
|
缓存 安全
并发编程学习一
并发编程学习一
80 0
|
Java 调度 C++
JUC 并发编程学习笔记(上)
JUC 并发编程学习笔记(上)
86 0
JUC 并发编程学习笔记(上)