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>
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 修饰,效率相对低。
2:CopyOnWriteArrayList 的方法 都没被 synchronized 修饰,效率相对较高。
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
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
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
4:常用的辅助类
4.1 CountDownLatch
4.1.1 介绍(减法计数器)
允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。
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 介绍(加法计数器)
源码:
//接收一个计数,一个线程参数 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 介绍(信号量)
源码:
默认是非公平锁,但是可以设置为公平锁
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离开车位