台州黄岩住房和城乡建设网站,网络推广做哪个网站比较好,wordpress增加板块,微信公众号小程序制作流程秋招Java面试大纲#xff1a;Java并发spring数据库RedisJVMNetty等疫情期间“闭关修炼”#xff0c;吃透这本Java核心知识#xff0c;跳槽面试不心慌Spring全家桶笔记#xff1a;SpringSpring BootSpring CloudSpring MVC前言Java集合是我认为在Java基础中最最重要的知识点…秋招Java面试大纲Java并发spring数据库RedisJVMNetty等疫情期间“闭关修炼”吃透这本Java核心知识跳槽面试不心慌Spring全家桶笔记SpringSpring BootSpring CloudSpring MVC前言Java集合是我认为在Java基础中最最重要的知识点了Java集合是必须掌握的。我在实习/秋招面试的时候只要是面到Java那一定是少不了Java集合。作为一个新人最关心的其实有一点这个技术在工作中是怎么用的。换个说法“工作中常用到的Java集合有哪些应用场景是什么”List集合List集合下最常见的集合类有两个ArrayList和LinkedList在工作中我都是无脑用ArrayList。我问了两个同事“你们在项目中用过LinkedList吗”他们都表示没有。众所周知ArrayList底层是数组LinkedList底层是链表。数组遍历速度快LinkedList增删元素快。为什么在工作中一般就用ArrayList而不用LinkedList呢原因也很简单在工作中遍历的需求比增删多即便是增加元素往往也只是从尾部插入元素而ArrayList在尾部插入元素也是O(1)ArrayList增删没有想象中慢ArrayList的增删底层调用的copyOf()被优化过加上现代CPU对内存可以块操作普通大小的ArrayList增删比LinkedList更快。所以在开发中想到要用集合来装载元素第一个想到的就是ArrayList。那么来了LinkedList用在什么地方呢我们一般用在刷算法题上。把LinkedList当做一个先进先出的队列LinkedList本身就实现了Queue接口如果考虑线程安全的问题可以看看CopyWriteOnArrayList实际开发用得不多但我觉得可以了解一下它的思想(CopyWriteOn)这个思想在Linux/文件系统都有用到。Set集合Set集合下最常见的集合类有三个HashSet、TreeSet、LinkedHashSetList和Set都是集合一般来说如果我们需要保证集合的元素是唯一的就应该想到用Set集合比如说现在要发送一批消息给用户我们为了减少「一次发送重复的内容给用户」这样的错误我们就用Set集合来保存用户的userId/phone自然地首先要保证最上游的那批用户的userId/phone是没有重复的而我们用Set集合只是为了做一个兜底来尽可能避免重复发送的问题。一般我们在开发中最多用到的也就是HashSet。TreeSet是可以排序的Set一般我们需要有序从数据库拉出来的数据就是有序的可能往往写order by id desc比较多。而在开发中也很少管元素插入有序的问题所以LinkedHashSet一般也用不上。如果考虑线程安全的问题可以考虑CopyOnWriteArraySet用得就更少了(这是一个线程安全的Set底层实际上就是CopyWriteOnArrayList)TreeSet和LinkedHashSet更多的可能用在刷算法的时候。Map集合Map集合最常见的子类也有三个HashMap、LinkedHashMap、TreeMap如果考虑线程安全问题应该想到的是ConcurrentHashMap当然了Hashtable也要有一定的了解因为面试实在是问得太多太多了。HashMap在实际开发中用得也非常多只要是key-value结构的一般我们就用HashMap。LinkedHashMap和TreeMap用的不多原因跟HashSet和TreeSet一样。ConcurrentHashMap在实际开发中也用得挺多我们很多时候把ConcurrentHashMap用于本地缓存不想每次都网络请求数据在本地做本地缓存。监听数据的变化如果数据有变动了就把ConcurrentHashMap对应的值给更新了。Queue队列不知道大家有没有学过生产者和消费者模式秋招面试的时候可能会让你手写一段这样的代码。最简单的方式就是用阻塞队列去写。类似下面生产者import java.util.Random;import java.util.Vector;import java.util.concurrent.atomic.AtomicInteger;public class Producer implements Runnable { // true---生产者一直执行false---停掉生产者 private volatile boolean isRunning true; // 公共资源 private final Vector sharedQueue; // 公共资源的最大数量 private final int SIZE; // 生产数据 private static AtomicInteger count new AtomicInteger(); public Producer(Vector sharedQueue, int SIZE) { this.sharedQueue sharedQueue; this.SIZE SIZE; } Override public void run() { int data; Random r new Random(); System.out.println(start producer id Thread.currentThread().getId()); try { while (isRunning) { // 模拟延迟 Thread.sleep(r.nextInt(1000)); // 当队列满时阻塞等待 while (sharedQueue.size() SIZE) { synchronized (sharedQueue) { System.out.println(Queue is full, producer Thread.currentThread().getId() is waiting, size sharedQueue.size()); sharedQueue.wait(); } } // 队列不满时持续创造新元素 synchronized (sharedQueue) { // 生产数据 data count.incrementAndGet(); sharedQueue.add(data); System.out.println(producer create data: data , size sharedQueue.size()); sharedQueue.notifyAll(); } } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupted(); } } public void stop() { isRunning false; }}消费者import java.util.Random;import java.util.Vector;public class Consumer implements Runnable { // 公共资源 private final Vector sharedQueue; public Consumer(Vector sharedQueue) { this.sharedQueue sharedQueue; } Override public void run() { Random r new Random(); System.out.println(start consumer id Thread.currentThread().getId()); try { while (true) { // 模拟延迟 Thread.sleep(r.nextInt(1000)); // 当队列空时阻塞等待 while (sharedQueue.isEmpty()) { synchronized (sharedQueue) { System.out.println(Queue is empty, consumer Thread.currentThread().getId() is waiting, size sharedQueue.size()); sharedQueue.wait(); } } // 队列不空时持续消费元素 synchronized (sharedQueue) { System.out.println(consumer consume data sharedQueue.remove(0) , size sharedQueue.size()); sharedQueue.notifyAll(); } } } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } }}Main方法测试import java.util.Vector;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class Test2 { public static void main(String[] args) throws InterruptedException { // 1.构建内存缓冲区 Vector sharedQueue new Vector(); int size 4; // 2.建立线程池和线程 ExecutorService service Executors.newCachedThreadPool(); Producer prodThread1 new Producer(sharedQueue, size); Producer prodThread2 new Producer(sharedQueue, size); Producer prodThread3 new Producer(sharedQueue, size); Consumer consThread1 new Consumer(sharedQueue); Consumer consThread2 new Consumer(sharedQueue); Consumer consThread3 new Consumer(sharedQueue); service.execute(prodThread1); service.execute(prodThread2); service.execute(prodThread3); service.execute(consThread1); service.execute(consThread2); service.execute(consThread3); // 3.睡一会儿然后尝试停止生产者(结束循环) Thread.sleep(10 * 1000); prodThread1.stop(); prodThread2.stop(); prodThread3.stop(); // 4.再睡一会儿关闭线程池 Thread.sleep(3000); // 5.shutdown()等待任务执行完才中断线程(因为消费者一直在运行的所以会发现程序无法结束) service.shutdown(); }}我的项目用阻塞队列也挺多的(我觉得跟个人编写的代码风格习惯有关)类似实现了上面的生产者和消费者模式。真实场景例子运营要发一条推送消息首先需要去用户画像系统圈选一个人群填写对应的人群ID和发送时间。我通过时间调度通过RPC拿到人群的信息。遍历HDFS得到这个人群的每个userId将遍历的userId放到一个阻塞队列里边去用多个线程while(true)取阻塞队列的数据好处是什么我在取userId的时候会有个限制要么超出了指定的时间要么达到BatchSize的值。这样我就可以将相同内容的不同userId组成一个Task。本来100个userId是100个Task现在我将100个userId放在一个Task里边(因为发送的内容是相同的所以我可以这么干)。这样再往下游传的时候并发量就降低了很多。什么时候考虑线程安全什么时候考虑线程安全的集合类那当然是线程不安全的时候咯。那什么时候线程不安全最常见的是操作的对象是有状态的虽然说我们经常会听到线程不安全但在业务开发中要我们程序员处理线程不安全的地方少之又少。比如说你在写Servlet的时候加过syn/lock锁吗应该没有吧因为我们的操作的对象往往是无状态的。没有共享变量被多个线程访问自然就没有线程安全问题了。SpringMVC是单例的但SpringMVC都是在方法内操作数据的每个线程进入方法都会生成栈帧每个栈帧的数据都是线程独有的如果不设定共享变量不会有线程安全问题。上面只是简单举了SpringMVC的例子(只是为了更好的理解)一句话总结只要涉及到多个线程操作一个共享变量的时候就要考虑是不是要用线程安全的集合类。最后还是想强调一下Java集合虽然在工作中不是每个都经常用得到但是还是得重点学习学习。如果你学习到了源码可能你在创建集合的时候就会指定了集合的大小(即便我们知道它能动态扩容)如果你想要去面试Java集合是肯定少不了的必问的一个知识点你学会了就是送分题。现在已经工作有一段时间了为什么还来写Java集合呢原因有以下几个我是一个对排版有追求的人如果早期关注我的同学可能会发现我的GitHub、文章导航的read.me会经常更换。现在的GitHub导航也不合我心意了(太长了)并且早期的文章说实话排版也不太行我决定重新搞一波。我的文章会分发好几个平台但文章发完了可能就没人看了并且图床很可能因为平台的防盗链就挂掉了。又因为有很多的读者问我”你能不能把你的文章转成PDF啊“我写过很多系列级的文章这些文章就几乎不会有太大的改动了就非常适合把它们给”持久化“。作者Java3y原文链接https://juejin.im/post/5e7c05236fb9a009a6764ef9