2020大厂面试JUC线程重要技术点【集合+线程+阻塞队列+线程池】(上)

简介: 2020大厂面试JUC线程重要技术点【集合+线程+阻塞队列+线程池】(上)

一、集合安全问题


1.1 ArrayList


  • 空的集合初始值为10
  • object类型的数组
  • 扩容Arrays.copyOf 原始大小的一倍
  • 线程不安全


1.1.1 不安全

java.util.concurrentModificationException

20200720135836521.png


  • 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():


20200720151403239.png


解决办法二CopyOnWriteArraySet<>():

20200720151548562.png

1.3 HashMap


演示错误 java.util.concurrentModificationException

20200720152506746.png

解决办法一:


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()+"班长最后关门走人");
  }
}

2020072114222671.png


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()+"班长锁门,最后关门走人");
  }
}

20200721142611649.png


四、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();
    }
  }
}

20200721151044492.png


五、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();
    }
  }
}

20200721153122928.png

六、阻塞队列【MQ核心】

20200721154101133.png

20200721154829144.png

6.1 阻塞队列ArrayBlockingQueue<>()

20200721160542467.png

报异常

20200721163925468.png

没有异常,直接返回布尔类型false

20200721164245788.png

一直阻塞,取出用take方法


20200721165142895.png

过时不候


6.2 阻塞队列 SynchronousQueue<>()

20200721165636702.png

目录
相关文章
|
5天前
|
决策智能
集合-Nim游戏(面试hard博弈论)
集合-Nim游戏(面试hard博弈论)
|
5天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
17 1
|
3天前
|
存储 索引 Python
【python学习】列表、元组、字典、集合,秋招是不是得到处面试
【python学习】列表、元组、字典、集合,秋招是不是得到处面试
|
3天前
|
Java 测试技术 开发工具
Android 笔记:AndroidTrain , Lint , build(1),只需一篇文章吃透Android多线程技术
Android 笔记:AndroidTrain , Lint , build(1),只需一篇文章吃透Android多线程技术
|
5天前
|
Java
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
24 0
|
5天前
|
设计模式 消息中间件 安全
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列
12 0
|
5天前
|
消息中间件 监控 前端开发
面试官:核心线程数为0时,线程池如何执行?
线程池是 Java 中用于提升程序执行效率的主要手段,也是并发编程中的核心实现技术,并且它也被广泛的应用在日常项目的开发之中。那问题来了,如果把线程池中的核心线程数设置为 0 时,线程池是如何执行的? 要回答这个问题,我们首先要了解在正常情况下,线程池的执行流程,也就是说当有一个任务来了之后,线程池是如何运行的? ## 1.线程池的执行流程 正常情况下(核心线程数不为 0 的情况下)线程池的执行流程如下: 1. **判断核心线程数**:先判断当前工作线程数是否大于核心线程数,如果结果为 false,则新建线程并执行任务。 2. **判断任务队列**:如果大于核心线程数,则判断任务队列是否
24 1
面试官:核心线程数为0时,线程池如何执行?
|
5天前
|
存储 缓存 前端开发
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
Java串口通信技术探究3:RXTX库线程 优化系统性能的SerialPortEventListener类
56 3
|
5天前
|
Dart 前端开发 安全
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
【4月更文挑战第30天】本文探讨了Flutter中线程管理和并发编程的关键性,强调其对应用性能和用户体验的影响。Dart语言提供了`async`、`await`、`Stream`和`Future`等原生异步支持。Flutter采用事件驱动的单线程模型,通过`Isolate`实现线程隔离。实践中,可利用`async/await`、`StreamBuilder`和`Isolate`处理异步任务,同时注意线程安全和性能调优。参考文献包括Dart异步编程、Flutter线程模型和DevTools文档。
【Flutter前端技术开发专栏】Flutter中的线程与并发编程实践
|
5天前
|
存储 安全 Java
【亮剑】`ConcurrentHashMap`是Java中线程安全的哈希表,采用锁定分离技术提高并发性能
【4月更文挑战第30天】`ConcurrentHashMap`是Java中线程安全的哈希表,采用锁定分离技术提高并发性能。数据被分割成多个Segment,每个拥有独立锁,允许多线程并发访问不同Segment。当写操作发生时,计算键的哈希值定位Segment并获取其锁;读操作通常无需锁定。内部会根据负载动态调整Segment,减少锁竞争。虽然使用不公平锁,但Java 8及以上版本提供了公平锁选项。理解其工作原理对开发高性能并发应用至关重要。