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

目录
相关文章
|
1天前
|
Java 数据库 Android开发
【专栏】Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理
【4月更文挑战第27天】本文探讨了Kotlin在Android开发中的多线程优化,包括线程池、协程的使用,任务分解、避免阻塞操作以及资源管理。通过案例分析展示了网络请求、图像处理和数据库操作的优化实践。同时,文章指出并发编程的挑战,如性能评估、调试及兼容性问题,并强调了多线程优化对提升应用性能的重要性。开发者应持续学习和探索新的优化策略,以适应移动应用市场的竞争需求。
|
1天前
|
Java 数据库
【Java多线程】对线程池的理解并模拟实现线程池
【Java多线程】对线程池的理解并模拟实现线程池
14 1
|
1天前
|
Java 程序员 数据库
Java线程池让使用线程变得更加高效
使用一个线程需要经过创建、运行、销毁三大步骤,如果业务系统每个线程都要经历这个过程,那会带来过多不必要的资源消耗。线程池就是为了解决这个问题而生,需要时就从池中拿取,使用完毕就放回去,池化思想通过复用对象大大提高了系统的性能。线程池、数据库连接池、对象池等都采用了池化技术,下面我们就来学习下线程池的核心知识、面试重点~
61 5
Java线程池让使用线程变得更加高效
|
1天前
|
监控 Java 测试技术
【技术面试】服务器常问面试集锦
【技术面试】服务器常问面试集锦
9 2
|
1天前
|
Java
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类
19 0
|
1天前
|
消息中间件 监控 前端开发
面试官:核心线程数为0时,线程池如何执行?
线程池是 Java 中用于提升程序执行效率的主要手段,也是并发编程中的核心实现技术,并且它也被广泛的应用在日常项目的开发之中。那问题来了,如果把线程池中的核心线程数设置为 0 时,线程池是如何执行的? 要回答这个问题,我们首先要了解在正常情况下,线程池的执行流程,也就是说当有一个任务来了之后,线程池是如何运行的? ## 1.线程池的执行流程 正常情况下(核心线程数不为 0 的情况下)线程池的执行流程如下: 1. **判断核心线程数**:先判断当前工作线程数是否大于核心线程数,如果结果为 false,则新建线程并执行任务。 2. **判断任务队列**:如果大于核心线程数,则判断任务队列是否
19 1
面试官:核心线程数为0时,线程池如何执行?
|
1天前
|
存储 监控 安全
21个 JVM 技术点详解(附面试解答)
以上V哥给大家详细介绍了 JVM 中涉及的21个点,全网还没有针对 JVM 这样来梳理的内容,希望对你深入了解 JVM 有一定帮助,另,V 哥给大家推荐一本《深入 JVM 虚拟机》的书籍,可以作为工具书使用,高阶的 Java 程序员几乎人手一本。今天的分享就到这里,任何疑问欢迎与 V 哥一起交流,畅谈 Java 人生。
|
1天前
|
监控 安全 Java
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
【多线程学习】深入探究阻塞队列与生产者消费者模型和线程池常见面试题
|
1天前
|
监控 Java 调度
Java多线程实战-从零手搓一个简易线程池(四)线程池生命周期状态流转实现
Java多线程实战-从零手搓一个简易线程池(四)线程池生命周期状态流转实现
|
1天前
|
设计模式 Java
Java多线程实战-从零手搓一个简易线程池(三)线程工厂,核心线程与非核心线程逻辑实现
Java多线程实战-从零手搓一个简易线程池(三)线程工厂,核心线程与非核心线程逻辑实现