一、集合安全问题
1.1 ArrayList
- 空的集合初始值为10
- object类型的数组
- 扩容Arrays.copyOf 原始大小的一倍
- 线程不安全
1.1.1 不安全
java.util.concurrentModificationException
- Vector加了锁保证了数据一致性,但是并发性急剧下降,所以很少用!
- ArrayList牺牲了线程安全从而保证并发性
1.1.2 如何解决ArrayList线程不安全问题
1.new Vector<>()
2.Collection与Collections
- Collection为集合类的父接口
- Collections为辅助类来解决ArrayList线程不安全问题
List<String> list = Collections.synchronizedList(new ArrayList<>());
3.CopyOnWriteArrayList<>()类
写时复制 读写分离的思想
List<String> list = new CopyOnWriteArrayList<>();
private tranisent volatile []…
1.2 HashSet
底层:HashMap 初始值16 负载因子0.75
线程不安全解决的问题与上面雷同
解决办法一Collections.synchronizedSet():
解决办法二CopyOnWriteArraySet<>():
1.3 HashMap
演示错误 java.util.concurrentModificationException
解决办法一:
Map<String,String> map = new ConcurrentHashMap<>();
解决办法二:
Collections.synchronizedMap();
二、JAVA锁机制
公平锁:多个新线程按照申请顺序来获取锁,先到先得 非
非公平锁:多个线程并不是按照申请的顺序,有可能造成优先级反转或者饥饿现象。
2.1 可重入锁【递归锁】
ReentrantLock
线程可以进入任何一个它已经拥有的锁同步着的代码块
通过构造函数制定该锁是否为公平锁,默认是非公平锁。
非公平锁的优势在于吞吐量比较大
对于Synchronized而言,也是一种非公平锁
作用:避免死锁
2.2 自旋锁
是指尝试获取锁的线程不会阻塞,而是采用循环的方式来尝试乎获取锁,这样的好处就是减少线程上下文的切换消耗,缺点是循环会消耗CPU. do while() CAS 期望值与工作区间的值比较
2.3 独占锁(写锁)/共享锁(读锁)
独占锁:指该锁一次只能被一个线程所持有的。
ReentrantLock Synchronized 都是独占锁
共享锁:该锁可以被多个线程所持有
ReentrantReadWriteLock为共享锁,写锁为独占锁
读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程都是互斥的。
一个线程去写【原子+独占】绝对不可以被阻断,多个线程可以读
【问题描述如下:】
class MyCache{//缓存资源类 //volatile 可见性 不保证原子性 禁止指令重排 private volatile Map<String,Object> map = new HashMap<>(); //解决问题 原子性 //private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock (); public void put(String key,Object value){ //加写锁 rwLock.writeLock().lock(); try{ System.out.println(Thread.currentThread().getName()+"正在写入"+key); try{Time.MILLSECONDS.sleep(300);}catch(){}; map.put(key,value); System.out.println(Thread.currentThread().getName()+"写入完成"); }catch(Exception e){ }finally{ rwLock.writeLock().unlock(); } } public void get(String key,Object value){ //加读锁 rwLock.readLock().lock(); try{ System.out.println(Thread.currentThread().getName()+"正在读取"+key); try{Time.MILLSECONDS.sleep(300);}catch(){}; Object result = map.get(key); System.out.println(Thread.currentThread().getName()+"读取完成"+result); }catch(Exception e){ }finally{ rwLock.readLock().unlock(); } } } public class ReadWriteLockDemo{ public static void main(String[] args){ MyCache myCache = new MyCache(); //写 for(int i = 1;i <= 5;i++){ new Thread(() -> { final int tempInt = i; myCache.put(tempInt+"",tempInt+""); },String.valueOf(i)).start(); } //读 for(int i = 1;i <= 5;i++){ new Thread(() -> { final int tempInt = i; myCache.get(tempInt+""); },String.valueOf(i)).start(); } } }
这样既保证了数据一致性,有保证了并发性,读写分离。
Synchronized太重量。
三、CountDownLatch【线程做减法倒计时】
3.1 离开教室锁门问题产生!
public class CountDownLatchDemo{ public static void main(String[] args){ for(int i = 1;i<=6;i++){ new Thread(()->{ System.out.println(Thread.currentThread().getName()+"上完自习,离开教室"); },String.valueOf(i)).start(); } System.out,println(Thread.currentThread().getName()+"班长最后关门走人"); } }
3.2 解决问题:CountDownLatch
public class CountDownLatchDemo{ public static void main(String[] args){ // 计数 CountDownLatch countDownLatch = new CountDownLatch(6); for(int i = 1;i<=6;i++){ new Thread(()->{ System.out.println(Thread.currentThread().getName()+"上完自习,离开教室"); countDownLatch.countDown();//减1操作 },String.valueOf(i)).start(); } // 主线程等待 countDownLatch.await(); System.out,println(Thread.currentThread().getName()+"班长锁门,最后关门走人"); } }
四、CyclicBarrier【加法】
加法 与CountDownLatch【减法】相反
加到一定的数值然后做事
最后一个线程到达屏障时候才会进行
public class CountDownLatchDemo{ public static void main(String[] args){ CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{System.out.println("***召唤神龙***");}); for(int i = 1;i<=7;i++){ final int tempInt = i; new Thread(()->{ System.out.println(Thread.currentThread().getName()+"收集到第"+tempInt +"颗龙珠"); cyclicBarrier.await(); },String.valueOf(i)).start(); } } }
五、Semaphore【信号量】
多个共享资源的互斥使用
并发线程数量的控制
public class CountDownLatchDemo{ public static void main(String[] args){ // 模拟3个停车位 Semaphore semaphore = new Semaphore(3); for(int i = 1; i <= 6;i++){ new Thread(()->{ try{ semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"抢到车位"); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName()+"停车3S后,离开车位"); }catch(...){ }finally{ semaphore.release(); } },String.valueOf(i)).start(); } } }
六、阻塞队列【MQ核心】
6.1 阻塞队列ArrayBlockingQueue<>()
报异常
没有异常,直接返回布尔类型false
一直阻塞,取出用take方法
过时不候
6.2 阻塞队列 SynchronousQueue<>()