2026/1/14 11:16:43
网站建设
项目流程
怀柔网站建设,高端品牌车,网站建设推广合同范本,海口网红图书馆文章目录
Java 数据结构 1. 基本数据结构 1.1 数组 (Array)1.2 链表 (Linked List)1.3 栈 (Stack)1.4 队列 (Queue) 双向队列优先级队列 2. 树形数据结构 2.1 二叉树 (Binary Tree)2.2 堆 (Heap) 3. 散列数据结构 3.1 哈希表 (Hash Map)3.2 LinkedHashMap3.3 TreeMapConcurre…文章目录Java 数据结构1. 基本数据结构1.1 数组 (Array)1.2 链表 (Linked List)1.3 栈 (Stack)1.4 队列 (Queue)双向队列优先级队列2. 树形数据结构2.1 二叉树 (Binary Tree)2.2 堆 (Heap)3. 散列数据结构3.1 哈希表 (Hash Map)3.2 LinkedHashMap3.3 TreeMapConcurrentHashMap详解ConcurrentHashMap - JDK 1.7ConcurrentHashMap - JDK 1.84. 并发数据结构JUC集合4.1 ConcurrentHashMap4.2 CopyOnWriteArrayList4.3 ConcurrentLinkedQueue4.4 BlockingQueueArrayBlockingQueue:LinkedBlockingQueue:PriorityBlockingQueue:DelayQueue:SynchronousQueue:Java 数据结构详细请转到pdai的博客1. 基本数据结构1.1 数组 (Array)数组的优点:存取速度快数组的缺点:事先必须知道数组的长度插入删除元素很慢空间通常是有限制的需要大块连续的内存块插入删除元素的效率很低源码分析1、底层数据结构是Objecttransient Object[] elementData; private int size;2、构造函数包括无参构造和有参数构造有参构造时指定构造大小或者直接复制已有的public ArrayList(int initialCapacity) public ArrayList() public ArrayList(Collection? extends E c)其中复制方式public ArrayList(Collection? extends E c)底层实现是通过Arrays.copyof接口来实现的elementData Arrays.copyOf(elementData, size, Object[].class);3、扩容机制默认情况下ArrayList的容量为10是由以下参数决定的private static final int DEFAULT_CAPACITY 10;扩容机制触发条件空间已满扩容大小1.5倍源码调用路径其中在add的时候会调用 this.ensureCapacityInternal(this.size 1);其中size1是当前添加后的容量值传入ensureExplicitCapacity方法源码如下private void ensureExplicitCapacity(int minCapacity) { this.modCount; //如果容量不足 if (minCapacity - this.elementData.length 0) { this.grow(minCapacity); }这里可以看到容量不足时会调用 this.grow(minCapacity);grow方式是最关键的方法private void grow(int minCapacity) { int oldCapacity this.elementData.length; //扩容1.5倍 int newCapacity oldCapacity (oldCapacity 1); if (newCapacity - minCapacity 0) { newCapacity minCapacity; } //如果超过这个大容量要单独处理return minCapacity 2147483639 ? 2147483647 : 2147483639; if (newCapacity - 2147483639 0) { newCapacity hugeCapacity(minCapacity); } //先把之前的copy过去 this.elementData Arrays.copyOf(this.elementData, newCapacity); }扩完容之后就可以直接放入数据了public boolean add(E e) { this.ensureCapacityInternal(this.size 1); this.elementData[this.size] e; return true; }4、为什么remove性能不好因为整体要平移直接看源码public boolean remove(Object o) { int index; if (o null) { for(index 0; index this.size; index) { if (this.elementData[index] null) { this.fastRemove(index); return true; } } } else { for(index 0; index this.size; index) { if (o.equals(this.elementData[index])) { //效率较高的数组拷贝方式,调用了System.arraycopy实际上是一种o(n)的复杂度快速改变索引的方式 this.fastRemove(index); return true; } } } return false; }整体平移的时候通过一个for循环挨个执行时间复杂度是o(n)加上this.fastRemove(index);是一种o(n)的拷贝方式效率可想而知5、其他APIindexOf(), lastIndexOf():获取元素的第一次出现的index。get()获取值set()设置值addAll()添加大部分数据6、优化在使用ArrayList时如果新增数据后面逐渐删除会导致当前数组占用空间过大无法清理实际上里面只存储了几个数据的情况因此出现这种情况可以利用jdk提供的方法优化trimToSize()底层数组的容量调整为当前列表保存的实际元素的大小的功能源码如下public void trimToSize() { modCount; if (size elementData.length) { elementData (size 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }1.2 链表 (Linked List)LinkedList同时实现了List接口和Deque接口也就是说它既可以看作一个顺序容器又可以看作一个队列(Queue)同时又可以看作一个栈(Stack)。这样看来LinkedList简直就是个全能冠军。当你需要使用栈或者队列时可以考虑使用LinkedList一方面是因为Java官方已经声明不建议使用Stack类更遗憾的是Java里根本没有一个叫做Queue的类(它是个接口名字)。关于栈或队列现在的首选是ArrayDeque它有着比LinkedList(当作栈或队列使用时)有着更好的性能。整体上这是一个双向链表结构在Linkedlist定义了三个数据transient int size;//当前数目 transient LinkedList.NodeE first;//指向链表的第一个 transient LinkedList.NodeE last;//指向链表的最后一个元素这个链表的每个节点的数据结构Node(LinkedList.NodeE prev, E element, LinkedList.NodeE next) { this.item element;//当前元素值 this.next next;//后驱 this.prev prev;//前驱 }1、删除为什么这么快removeFirst(), removeLast(), remove(e), remove(index)这些方法用的是public boolean remove(Object o) { if (o null) { for (NodeE x first; x ! null; x x.next) { if (x.item null) { unlink(x); return true; } } } else { for (NodeE x first; x ! null; x x.next) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; }核心方法为unlink这个方法实际上是改变x这个节点的前驱指向x的后驱同时将x的指向都变成了null实际上是个o(1)复杂度的代码E unlink(NodeE x) { // assert x ! null; final E element x.item; final NodeE next x.next; final NodeE prev x.prev; if (prev null) {// 第一个元素 first next; } else { prev.next next; x.prev null; } if (next null) {// 最后一个元素 last prev; } else { next.prev prev; x.next null; } x.item null; // GC size--; modCount; return element; }2、add为什么不需要扩容改变指向就好了可以无限增长add(E e)、add(int index, E element)3、get()效率比较低查找操作的本质是查找元素的下标如源码所示会从first遍历整个list时间复杂度是o(n)LinkedList.NodeE node(int index) { LinkedList.Node x; int i; if (index this.size 1) { x this.first; for(i 0; i index; i) { x x.next; } return x; } else { x this.last; for(i this.size - 1; i index; --i) { x x.prev; } return x; } }4、其他APIgetFirst(), getLast()获取第一个元素 和获取最后一个元素:Queue 方法peek()本质上获取第一个元素poll()本质上将数据从第一个元素删除并获取第一个元素remove()调用的是removeFirst()offer(E e)调用的是add(e)element()调用的是getFirst()Deque 方法:offerFirst(E e)调用addFirst(e)offerLast(E e)调用addLast(e)peekFirst() 获取fist节点的值peekLast()获取last节点的值pollFirst()删除第一个节点pollLast()删除最后一个节点push(E e)添加作为第一个节点pop()弹出第一个节点1.3 栈 (Stack)栈本质上是一个加了锁的数组Vector通过定义规则的方式实现了先进后出逻辑但是这个Vector方法由于性能问题已经算是个过时的接口因此Stack也不再建议使用这里不再多说官方更建议将ArrayDeque来作为栈和队列来使用。1.4 队列 (Queue)双向队列Deque是double ended queue, 表示双向的队列英文读作deck. Deque 继承自 Queue接口,除了支持Queue的方法之外还支持insert, remove和examine操作由于Deque是双向的所以可以对队列的头和尾都进行操作它同时也支持两组格式一组是抛出异常的实现另外一组是返回值的实现(没有则返回null)。当把Deque当做FIFO的queue来使用时元素是从deque的尾部添加从头部进行删除的 所以deque的部分方法是和queue是等同的。具体如下://add调用的方法添加元素到尾部 public void addLast(E e) { if (e null) { throw new NullPointerException(); } else { this.elements[this.tail] e; if ((this.tail this.tail 1 this.elements.length - 1) this.head) { this.doubleCapacity(); } } } //poll调用的方法从头部获取元素 public E pollFirst() { int h this.head; E result this.elements[h]; if (result null) { return null; } else { this.elements[h] null; this.head h 1 this.elements.length - 1; return result; } }ArrayDeque和LinkedList是Deque的两个通用实现由于官方更推荐使用AarryDeque用作栈和队列只是底层的数据结构不一样一个循环数组一个是双向链表。在这个循环数组中为了满足可以同时在数组两端插入或删除元素的需求数组的任何一点都可能被看作起点或者终点。优先级队列这个优先级队列实际上是一个满足堆特性的数组结构。默认情况下是个小顶堆。可以改为大顶堆PriorityQueueInteger maxHeap new PriorityQueue(Collections.reverseOrder());在介绍PriorityQueue之前首先需要明确树结构与数组结构的关系leftNo parentNo*21 rightNo parentNo*22 parentNo (nodeNo-1)/2对于数组来说需要了解其初始与扩容机制int newCapacity oldCapacity (oldCapacity 64 ? oldCapacity 2 : oldCapacity 1);oldCapacity 64 判断如果当前容量小于 64那么使用 oldCapacity 2 作为新容量。这是为了在数组容量较小时提供一些额外的空间以避免频繁扩容。oldCapacity 64 判断如果当前容量大于等于 64那么使用 oldCapacity 1相当于 oldCapacity / 2作为新容量。这是为了在容量较大时以较小的步长逐渐扩容降低扩容的速度。1.5倍2. 树形数据结构2.1 二叉树 (Binary Tree)TreeMap(红黑树的Map结构)2.2 堆 (Heap)PriorityQueue默认小顶堆可以改为大顶堆PriorityQueueInteger maxHeap new PriorityQueue(Collections.reverseOrder());3. 散列数据结构3.1 哈希表 (Hash Map)HashMap实现了Map接口即允许放入key为null的元素也允许插入value为null的元素除该类未实现同步外其余跟Hashtable大致相同跟TreeMap不同该容器不保证元素顺序根据需要该容器可能会对元素重新哈希元素的顺序也会被重新打散因此不同时间迭代同一个HashMap的顺序可能会不同。 根据对冲突的处理方式不同哈希表有两种实现方式一种开放地址方式(Open addressing)另一种是冲突链表方式(Separate chaining with linked lists)。resize() 方法用于初始化数组或数组扩容每次扩容后容量为原来的 2 倍并进行数据迁移。Java8 对 HashMap 进行了一些修改最大的不同就是利用了红黑树所以其由 数组链表红黑树 组成。根据 Java7 HashMap 的介绍我们知道查找的时候根据 hash 值我们能够快速定位到数组的具体下标但是之后的话需要顺着链表一个个比较下去才能找到我们需要的时间复杂度取决于链表的长度为 O(n)。为了降低这部分的开销在 Java8 中当链表中的元素达到了 8 个时会将链表转换为红黑树在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。插入put(K key, V value)方法是将指定的key, value对添加到map里。该方法首先会对map做一次查找看是否包含该元组如果已经包含则直接返回查找过程类似于getEntry()方法如果没有找到则会通过addEntry(int hash, K key, V value, int bucketIndex)方法插入新的entry插入方式为头插法。resize() 方法用于初始化数组或数组扩容每次扩容后容量为原来的 2 倍并进行数据迁移。删除remove(Object key)的作用是删除key值对应的entry该方法的具体逻辑是在removeEntryForKey(Object key)里实现的。removeEntryForKey()方法会首先找到key值对应的entry然后删除该entry(修改链表的相应引用)。查找过程跟getEntry()过程类似。获取计算 key 的 hash 值根据 hash 值找到对应数组下标: hash (length-1)判断数组该位置处的元素是否刚好就是我们要找的如果不是走第三步判断该元素类型是否是 TreeNode如果是用红黑树的方法取数据如果不是走第四步遍历链表直到找到相等(或equals)的 key3.2 LinkedHashMapLinkedHashMap 是 Java 中的一个集合类它是 HashMap 的子类。与普通的 HashMap 不同LinkedHashMap 保留了元素插入的顺序。它使用一个双向链表来维护元素的顺序该链表连接了所有的元素。因此当你迭代 LinkedHashMap 时元素的顺序与插入顺序一致。1、保留插入顺序 LinkedHashMap 会按照元素插入的顺序来维护元素的顺序这是通过内部的双向链表实现的。2、性能类似于 HashMap 在大多数操作上LinkedHashMap 的性能与 HashMap 类似。它使用哈希表来实现快速的查找、插入和删除操作。3、迭代顺序 迭代 LinkedHashMap 的时候元素的顺序是按照插入的顺序。这与 HashMap 不同后者的迭代顺序是不确定的。4、支持 LRU 缓存策略 LinkedHashMap 提供了一个构造函数允许你创建一个有限容量的 LinkedHashMap当达到容量上限时会根据最近最少使用的原则删除最不经常使用的元素。3.3 TreeMap转载TreeMap 源码解析TreeMap实现了SortedMap接口也就是说会按照key的大小顺序对Map中的元素进行排序key大小的评判可以通过其本身的自然顺序(natural ordering)也可以通过构造时传入的比较器(Comparator)。TreeMap底层通过红黑树(Red-Black tree)实现也就意味着containsKey(), get(), put(), remove()都有着log(n)的时间复杂度。补充红黑树红黑树是一种近似平衡的二叉查找树它能够确保任何一个节点的左右子树的高度差不会超过二者中较低那个的一倍。具体来说红黑树是满足如下条件的二叉查找树(binary search tree):1、每个节点要么是红色要么是黑色。3、根节点必须是黑色4、红色节点不能连续(也即是红色节点的孩子和父亲都不能是红色)。5、对于每个节点从该点至null(树尾端)的任何路径都含有相同个数的黑色节点。左旋(Rotate Left)左旋的过程是将x的右子树绕x逆时针旋转使得x的右子树成为x的父亲同时修改相关节点的引用。旋转之后二叉查找树的属性仍然满足。左旋代码private void rotateLeft(EntryK,V p) { if (p ! null) { EntryK,V r p.right; p.right r.left; if (r.left ! null) r.left.parent p; r.parent p.parent; if (p.parent null) root r; else if (p.parent.left p) p.parent.left r; else p.parent.right r; r.left p; p.parent r; } }右旋右旋的过程是将x的左子树绕x顺时针旋转使得x的左子树成为x的父亲同时修改相关节点的引用。旋转之后二叉查找树的属性仍然满足。TreeMap中右旋代码如下:private void rotateRight(EntryK,V p) { if (p ! null) { EntryK,V l p.left; p.left l.right; if (l.right ! null) l.right.parent p; l.parent p.parent; if (p.parent null) root l; else if (p.parent.right p) p.parent.right l; else p.parent.left l; l.right p; p.parent l; } }get方法get(Object key)方法根据指定的key值返回对应的value该方法调用了getEntry(Object key)得到相应的entry然后返回entry.value。因此getEntry()是算法的核心。算法思想是根据key的自然顺序(或者比较器顺序)对二叉查找树进行查找直到找到满足k.compareTo(p.key) 0的entry。final EntryK,V getEntry(Object key) { ...... if (key null)//不允许key值为null throw new NullPointerException(); Comparable? super K k (Comparable? super K) key;//使用元素的自然顺序 EntryK,V p root; while (p ! null) { int cmp k.compareTo(p.key); if (cmp 0)//向左找 p p.left; else if (cmp 0)//向右找 p p.right; else return p; } return null; }put()方法put(K key, V value)方法是将指定的key, value对添加到map里。该方法首先会对map做一次查找看是否包含该元组如果已经包含则直接返回查找过程类似于getEntry()方法如果没有找到则会在红黑树中插入新的entry如果插入之后破坏了红黑树的约束条件还需要进行调整(旋转改变某些节点的颜色)。public V put(K key, V value) { ...... int cmp; EntryK,V parent; if (key null) throw new NullPointerException(); Comparable? super K k (Comparable? super K) key;//使用元素的自然顺序 do { parent t; cmp k.compareTo(t.key); if (cmp 0) t t.left;//向左找 else if (cmp 0) t t.right;//向右找 else return t.setValue(value); } while (t ! null); EntryK,V e new Entry(key, value, parent);//创建并插入新的entry if (cmp 0) parent.left e; else parent.right e; fixAfterInsertion(e);//调整 size; return null; }上述代码的插入部分并不难理解: 首先在红黑树上找到合适的位置然后创建新的entry并插入(当然新插入的节点一定是树的叶子)。难点是调整函数fixAfterInsertion()前面已经说过调整往往需要1.改变某些节点的颜色2.对某些节点进行旋转。ConcurrentHashMap详解JDK1.7之前的ConcurrentHashMap使用分段锁机制实现JDK1.8则使用数组链表红黑树数据结构和CAS原子操作实现ConcurrentHashMap本文将分别介绍这两种方式的实现方案及其区别。ConcurrentHashMap - JDK 1.7ConcurrentHashMap在对象中保存了一个Segment数组即将整个Hash表划分为多个分段而每个Segment元素即每个分段则类似于一个Hashtable这样在执行put操作时首先根据hash算法定位到元素属于哪个Segment然后对该Segment加锁即可。因此ConcurrentHashMap在多线程并发编程中可是实现多线程put操作。Segment 通过继承 ReentrantLock 来进行加锁所以每次需要加锁的操作锁住的是一个 segment这样只要保证每个 Segment 是线程安全的也就实现了全局的线程安全。ConcurrentHashMap 有 16 个 Segments所以理论上这个时候最多可以同时支持 16 个线程并发写。初始化loadFactor: 负载因子是 0.75得出初始阈值为 1.5也就是以后插入第一个元素不会触发扩容插入第二个会进行第一次扩容initialCapacity: 初始容量并发问题安全性put 操作的线程安全性。使用了 CAS 来初始化 Segment 中的数组。添加节点到链表的操作是插入到表头的所以如果这个时候 get 操作在链表遍历的过程已经到了中间是不会影响的。当然另一个并发问题就是 get 操作在 put 之后需要保证刚刚插入表头的节点被读取这个依赖于 setEntryAt 方法中使用的 UNSAFE.putOrderedObject。扩容。扩容是新创建了数组然后进行迁移数据最后面将 newTable 设置给属性 table。所以如果 get 操作此时也在进行那么也没关系如果 get 先行那么就是在旧的 table 上做查询操作而 put 先行那么 put 操作的可见性保证就是 table 使用了 volatile 关键字。remove 操作的线程安全性。get 操作需要遍历链表但是 remove 操作会破坏链表。如果 remove 破坏的节点 get 操作已经过去了那么这里不存在任何问题。如果 remove 先破坏了一个节点分两种情况考虑。1、如果此节点是头节点那么需要将头节点的 next 设置为数组该位置的元素table 虽然使用了 volatile 修饰但是 volatile 并不能提供数组内部操作的可见性保证所以源码中使用了 UNSAFE 来操作数组请看方法 setEntryAt。2、如果要删除的节点不是头节点它会将要删除节点的后继节点接到前驱节点中这里的并发保证就是 next 属性是 volatile 的。ConcurrentHashMap - JDK 1.8在JDK1.8中ConcurrentHashMap选择了与HashMap类似的数组链表红黑树的方式实现而加锁则采用CAS和synchronized实现。4. 并发数据结构JUC集合4.1 ConcurrentHashMap4.2 CopyOnWriteArrayListCopyOnWriteArrayList实现了List接口List接口定义了对列表的基本操作同时实现了RandomAccess接口表示可以随机访问(数组具有随机访问的特性)同时实现了Cloneable接口表示可克隆同时也实现了Serializable接口表示可被序列化。CopyOnWriteArrayList 是 Java 中并发编程提供的线程安全的集合类之一它的特点是在进行写操作时会创建一个新的复制copy来保证线程安全。以下是 CopyOnWriteArrayList 的主要原理和特点1、写时复制 当有写操作如添加、删除元素发生时CopyOnWriteArrayList 会创建一个新的数组复制原数组在新数组上执行写操作然后将新数组替换原来的数组。这样可以确保写操作不会影响正在进行的读操作因为读操作仍然在原数组上进行。2、读操作不需要加锁 由于读操作不涉及修改集合内容而是在不变的原数组上进行因此读操作不需要加锁。这使得在读多写少的场景中CopyOnWriteArrayList 的性能相对较好。3、适用于读多写少的场景 CopyOnWriteArrayList 的设计适用于那些读操作频繁而写操作相对较少的场景。这是因为在写时需要进行数组复制可能会引起一些性能开销。4、弱一致性 CopyOnWriteArrayList 提供的是弱一致性即在写操作完成后对于读操作来说并不一定能立即看到最新的修改。这是因为读操作仍然可能在旧数组上进行直到写操作完成并新数组替换了旧数组。5、不支持并发修改的迭代器 由于写操作会创建新的数组旧数组上的迭代器可能会抛出 ConcurrentModificationException 异常。因此CopyOnWriteArrayList 的迭代器是不支持并发修改的。CopyOnWriteArrayList的缺陷和使用场景CopyOnWriteArrayList 有几个缺点由于写操作的时候需要拷贝数组会消耗内存如果原数组的内容比较多的情况下可能导致young gc或者full gc不能用于实时读的场景像拷贝数组、新增元素都需要时间所以调用一个set操作后读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求CopyOnWriteArrayList 合适读多写少的场景不过这类慎用因为谁也没法保证CopyOnWriteArrayList 到底要放置多少数据万一数据稍微有点多每次add/set都要重新复制数组这个代价实在太高昂了。在高性能的互联网应用中这种操作分分钟引起故障。4.3 ConcurrentLinkedQueueConcurerntLinkedQueue一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部队列获取操作从队列头部获得元素。当多个线程共享访问一个公共 collection 时ConcurrentLinkedQueue是一个恰当的选择。此队列不允许使用null元素。ConcurrentLinkedQueue 是 Java 中并发编程提供的线程安全的队列实现。它基于非阻塞算法使用 CASCompare and Swap等原子操作来确保线程安全。以下是 ConcurrentLinkedQueue 的主要特点和原理非阻塞算法 ConcurrentLinkedQueue 使用非阻塞算法避免了使用锁的情况因此具有较好的并发性能。它通过 CAS 操作等方式来保证多个线程可以同时进行读和写操作。1、无界队列 ConcurrentLinkedQueue 是无界队列它没有固定的容量限制。可以不受限制地添加元素。2、基于链表 内部使用单向链表来组织队列元素。新的元素总是被添加到队列的尾部而移除元素则发生在队列的头部。3、线程安全的入队和出队操作 ConcurrentLinkedQueue 提供了线程安全的入队offer和add和出队poll和remove操作。这意味着多个线程可以同时进行这些操作而不需要额外的同步。4、迭代器支持弱一致性 ConcurrentLinkedQueue 的迭代器是弱一致性的它不会抛出 ConcurrentModificationException 异常。因此迭代器可能看到队列的部分修改而不一定是最新状态。5、适用于生产者-消费者模型 ConcurrentLinkedQueue 在生产者-消费者模型中是一个常用的选择特别是当多个线程并发地生产和消费元素时。4.4 BlockingQueueBlockingQueue大家族有哪些? ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue, SynchronousQueue…BlockingQueue 通常用于一个线程生产对象而另外一个线程消费这些对象的场景。一个线程将会持续生产新对象并将其插入到队列之中直到队列达到它所能容纳的临界点。也就是说它是有限的。如果该阻塞队列到达了其临界点负责生产的线程将会在往里边插入新对象时发生阻塞。它会一直处于阻塞之中直到负责消费的线程从队列中拿走一个对象。负责消费的线程将会一直从该阻塞队列中拿出对象。如果消费线程尝试去从一个空的队列中提取对象的话这个消费线程将会处于阻塞之中直到一个生产线程把一个对象丢进队列。ArrayBlockingQueue:基于数组的阻塞队列实现。有界队列必须指定容量。使用独占锁实现线程安全。BlockingQueueInteger arrayBlockingQueue new ArrayBlockingQueue(10);LinkedBlockingQueue:基于链表的阻塞队列实现。可以选择有界或无界。如果不指定容量默认是无界队列。使用两个锁读锁和写锁实现线程安全。BlockingQueueString linkedBlockingQueue new LinkedBlockingQueue();PriorityBlockingQueue:一个支持优先级的无界阻塞队列。元素按照它们的自然顺序或者根据构造函数中提供的比较器来进行排序。不保证同优先级元素的顺序。BlockingQueueTask priorityBlockingQueue new PriorityBlockingQueue();DelayQueue:用于存放实现了 Delayed 接口的元素这些元素只能在其指定的延迟时间过后才能被消费。通常用于定时任务调度。BlockingQueueDelayedTask delayQueue new DelayQueue();SynchronousQueue:一个不存储元素的阻塞队列。生产者线程必须等待消费者线程取走元素反之亦然。用于直接传递任务或数据通常在生产者和消费者线程之间进行交互。BlockingQueueString synchronousQueue new SynchronousQueue();说真的这两年看着身边一个个搞Java、C、前端、数据、架构的开始卷大模型挺唏嘘的。大家最开始都是写接口、搞Spring Boot、连数据库、配Redis稳稳当当过日子。结果GPT、DeepSeek火了之后整条线上的人都开始有点慌了大家都在想“我是不是要学大模型不然这饭碗还能保多久”先给出最直接的答案一定要把现有的技术和大模型结合起来而不是抛弃你们现有技术掌握AI能力的Java工程师比纯Java岗要吃香的多。即使现在裁员、降薪、团队解散的比比皆是……但后续的趋势一定是AI应用落地大模型方向才是实现职业升级、提升薪资待遇的绝佳机遇如何学习AGI大模型作为一名热心肠的互联网老兵我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。因篇幅有限仅展示部分资料需要点击下方链接即可前往获取2025最新版CSDN大礼包《AGI大模型学习资源包》免费分享**一、2025最新大模型学习路线一个明确的学习路线可以帮助新人了解从哪里开始按照什么顺序学习以及需要掌握哪些知识点。大模型领域涉及的知识点非常广泛没有明确的学习路线可能会导致新人感到迷茫不知道应该专注于哪些内容。我们把学习路线分成L1到L4四个阶段一步步带你从入门到进阶从理论到实战。L1级别:AI大模型时代的华丽登场L1阶段我们会去了解大模型的基础知识以及大模型在各个行业的应用和分析学习理解大模型的核心原理关键技术以及大模型应用场景通过理论原理结合多个项目实战从提示工程基础到提示工程进阶掌握Prompt提示工程。L2级别AI大模型RAG应用开发工程L2阶段是我们的AI大模型RAG应用开发工程我们会去学习RAG检索增强生成包括Naive RAG、Advanced-RAG以及RAG性能评估还有GraphRAG在内的多个RAG热门项目的分析。L3级别大模型Agent应用架构进阶实践L3阶段大模型Agent应用架构进阶实现我们会去学习LangChain、 LIamaIndex框架也会学习到AutoGPT、 MetaGPT等多Agent系统打造我们自己的Agent智能体同时还可以学习到包括Coze、Dify在内的可视化工具的使用。L4级别大模型微调与私有化部署L4阶段大模型的微调和私有化部署我们会更加深入的探讨Transformer架构学习大模型的微调技术利用DeepSpeed、Lamam Factory等工具快速进行模型微调并通过Ollama、vLLM等推理部署框架实现模型的快速部署。整个大模型学习路线L1主要是对大模型的理论基础、生态以及提示词他的一个学习掌握而L3 L4更多的是通过项目实战来掌握大模型的应用开发针对以上大模型的学习路线我们也整理了对应的学习视频教程和配套的学习资料。二、大模型经典PDF书籍书籍和学习文档资料是学习大模型过程中必不可少的我们精选了一系列深入探讨大模型技术的书籍和学习文档它们由领域内的顶尖专家撰写内容全面、深入、详尽为你学习大模型提供坚实的理论基础。书籍含电子版PDF三、大模型视频教程对于很多自学或者没有基础的同学来说书籍这些纯文字类的学习教材会觉得比较晦涩难以理解因此我们提供了丰富的大模型视频教程以动态、形象的方式展示技术概念帮助你更快、更轻松地掌握核心知识。四、大模型项目实战学以致用当你的理论知识积累到一定程度就需要通过项目实战在实际操作中检验和巩固你所学到的知识同时为你找工作和职业发展打下坚实的基础。五、大模型面试题面试不仅是技术的较量更需要充分的准备。在你已经掌握了大模型技术之后就需要开始准备面试我们将提供精心整理的大模型面试题库涵盖当前面试中可能遇到的各种技术问题让你在面试中游刃有余。因篇幅有限仅展示部分资料需要点击下方链接即可前往获取2025最新版CSDN大礼包《AGI大模型学习资源包》免费分享