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

目录
相关文章
|
8月前
|
缓存 安全 Java
JUC系列之《CountDownLatch:同步多线程的精准发令枪 》
CountDownLatch是Java并发编程中用于线程协调的同步工具,通过计数器实现等待机制。主线程等待多个工作线程完成任务后再继续执行,适用于资源初始化、高并发模拟等场景,具有高效、灵活、线程安全的特点,是JUC包中实用的核心组件之一。
|
8月前
|
设计模式 缓存 安全
【JUC】(6)带你了解共享模型之 享元和不可变 模型并初步带你了解并发工具 线程池Pool,文章内还有饥饿问题、设计模式之工作线程的解决于实现
JUC专栏第六篇,本文带你了解两个共享模型:享元和不可变 模型,并初步带你了解并发工具 线程池Pool,文章中还有解决饥饿问题、设计模式之工作线程的实现
535 2
|
8月前
|
Java 测试技术 API
【JUC】(1)带你重新认识进程与线程!!让你深层次了解线程运行的睡眠与打断!!
JUC是什么?你可以说它就是研究Java方面的并发过程。本篇是JUC专栏的第一章!带你了解并行与并发、线程与程序、线程的启动与休眠、打断和等待!全是干货!快快快!
1152 2
|
8月前
|
设计模式 消息中间件 安全
【JUC】(3)常见的设计模式概念分析与多把锁使用场景!!理解线程状态转换条件!带你深入JUC!!文章全程笔记干货!!
JUC专栏第三篇,带你继续深入JUC! 本篇文章涵盖内容:保护性暂停、生产者与消费者、Park&unPark、线程转换条件、多把锁情况分析、可重入锁、顺序控制 笔记共享!!文章全程干货!
444 1
|
监控 Kubernetes Java
阿里面试:5000qps访问一个500ms的接口,如何设计线程池的核心线程数、最大线程数? 需要多少台机器?
本文由40岁老架构师尼恩撰写,针对一线互联网企业的高频面试题“如何确定系统的最佳线程数”进行系统化梳理。文章详细介绍了线程池设计的三个核心步骤:理论预估、压测验证和监控调整,并结合实际案例(5000qps、500ms响应时间、4核8G机器)给出具体参数设置建议。此外,还提供了《尼恩Java面试宝典PDF》等资源,帮助读者提升技术能力,顺利通过大厂面试。关注【技术自由圈】公众号,回复“领电子书”获取更多学习资料。
|
存储 缓存 安全
JUC并发—11.线程池源码分析
本文主要介绍了线程池的优势和JUC提供的线程池、ThreadPoolExecutor和Excutors创建的线程池、如何设计一个线程池、ThreadPoolExecutor线程池的执行流程、ThreadPoolExecutor的源码分析、如何合理设置线程池参数 + 定制线程池。
JUC并发—11.线程池源码分析
|
安全 Java 程序员
面试必看:如何设计一个可以优雅停止的线程?
嘿,大家好!我是小米。今天分享一篇关于“如何停止一个正在运行的线程”的面试干货。通过一次Java面试经历,我明白了停止线程不仅仅是技术问题,更是设计问题。Thread.stop()已被弃用,推荐使用Thread.interrupt()、标志位或ExecutorService来优雅地停止线程,避免资源泄漏和数据不一致。希望这篇文章能帮助你更好地理解Java多线程机制,面试顺利! 我是小米,喜欢分享技术的29岁程序员。欢迎关注我的微信公众号“软件求生”,获取更多技术干货!
388 53
|
数据采集 Java Linux
面试大神教你:如何巧妙回答线程优先级这个经典考题?
大家好,我是小米。本文通过故事讲解Java面试中常见的线程优先级问题。小明和小华的故事帮助理解线程优先级:高优先级线程更可能被调度执行,但并非越高越好。实际开发需权衡业务需求,合理设置优先级。掌握线程优先级不仅能写出高效代码,还能在面试中脱颖而出。最后,小张因深入分析成功拿下Offer。希望这篇文章能助你在面试中游刃有余!
286 4
面试大神教你:如何巧妙回答线程优先级这个经典考题?
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
830 14
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
613 13