皮皮网

皮皮网

【外卖订餐跑腿源码】【夜莺监控源码】【security源码大全】双端无后源码_双端无后源码是什么

时间:2024-12-25 14:49:18 分类:热点

1.˫?双端双端??޺?Դ??
2.Python 爬虫进阶篇——diskcahce缓存(二)
3.OkHttp3源码详解之 okhttp连接池复用机制(一)
4.一文详解 ArrayDeque 双端队列使用及实现原理
5.LinkedBlockingQueue

双端无后源码_双端无后源码是什么

˫???޺?Դ??

       fork/join框架在Java并发包中扮演着重要角色,尤其在Java 8的无后无后并行流中。本文将深入剖析其设计思路、源码源码核心角色和实现机制。双端双端

       首先,无后无后fork/join的源码源码外卖订餐跑腿源码工作原理是将大任务分解成小任务,并利用多核处理。双端双端其特殊之处在于运用了work-stealing算法,无后无后通过双端队列分配任务,源码源码即使线程处理完一个任务,双端双端也能从其他未完成的无后无后任务中“窃取”以提高效率。

       核心角色包括ForkJoinPool,源码源码作为任务的双端双端管理者和线程容器,负责任务的无后无后提交和workerThread的管理。ForkJoinWorkerThread则是源码源码实际执行任务的“工人”,处理队列中的任务,并通过work-stealing机制优化资源利用。WorkQueue是存放任务的双端队列,ForkJoinTask则定义了任务类型,分为有返回值和无返回值两种。

       在初始化阶段,ForkJoinPool通过ForkJoinWorkerThreadFactory创建线程,任务的提交逻辑分为首次提交和任务切分后提交。首次提交会确保队列的创建和加锁,任务切分则在workerThread中进行。任务的消费则由workerThread或非workerThread线程根据任务状态进行处理。

       至于任务的夜莺监控源码窃取,工作线程在run()方法中通过scan(WorkQueue, int r)函数实现,不断尝试从队列中“窃取”任务,直到找到或者遍历完所有队列。

       尽管文章只是概述,深入研究fork/join的源码是理解其内在机制的关键,这将有助于在实际开发中更有效地利用并发框架。

Python 爬虫进阶篇——diskcahce缓存(二)

       上一篇文章为大家介绍了diskcache的基础用法,本文将继续深入探讨diskcache的更多高级功能。

       关于diskcache,它是一种基于SQLite数据库的缓存对象管理方式。SQLite是一个轻量级的基于磁盘的数据库,它不需要单独的服务器进程,并支持SQL查询。在上篇文章的源码截图上,你可以看到一些SQL语句的使用。

       diskcache支持使用diskcache.FanoutCache自动分片基础数据库。分片是对数据进行水平分区,可以减少写入时的阻塞。尽管读和写不会互相阻碍,但写入会阻碍其他写入。分片的默认值为8。

       以下是一个示例代码:

       # 示例代码

       在示例中,我们在diskcache_2文件夹中创建了一个具有四个分片和一秒超时的缓存。如果操作耗时超过一秒,它们将尝试中止操作。

       那么每一个分片的security源码大全大小是多少呢?分片的大小都是平均分配的,占总空间的四分之一。diskcache的默认大小为1GB。

       diskcache提供了一个collections.deque兼容的双端队列diskcache.Deque。双端队列是堆栈和队列的一般化,可以在前后都可以进行快速访问和编辑,且可持久化,操作比较便捷。

       以下是一个示例代码:

       # 示例代码

       运行结果如下:

       Popleft 获取队列最前面的一个元素,pop获取末尾的一个元素;

       Appendleft 在队列最开始添加一个元素,append 在末尾添加一个元素;

       Extendleft 在队列最开始添加一个数组,extend在末尾添加一个数组;

       那么deque的存储位置在哪里?可以使用以下命令查看队列的存储位置:

       包括cache也是一个可以使用directory属性查看默认存储的位置;

       那么如何指定directory呢?Deque的第二个参数指定存储位置,第一个参数为队列的初始值。或者可以直接指定参数名称,如下:

       Index 类似于dict(字典),可持久化,使用也比较便捷。以下是一个示例:

       # 示例代码

       popitem表示获取最后一个键对值,并且删除,结果如下:

       peekitem() 可传递last 参数是否获取最后一个,不会删除原始值;

       setdefault(key,default) 可以给指定的key值设置默认值,在查找时可以预先设置一个默认值,防止key值不存在而抛出异常。

       keys()与values()可以查找所有Index中的key值与value值,然而没有提供判断key值是否存在的方法,但是可以使用setdefault的方法自行封装。

       Lock还记得上篇文章中提到的猴王源码商城Lock锁吗?先看一下源码:

       可以看到这里的锁用的就是cache的add方法的特性,源码中也给出了使用的方式:

       从源码上看有一个while 死循环,直到add成功时才会被释放,这里处理的方式不是很好,可能会造成死锁,所以一般情况下,都会添加一个过期的时间,如下:

       RLock还有一种RLock锁,与Lock锁的区别是RLock允许在同一线程中被多次acquire。而Lock却不允许这种情况。以下是一个示例:

       结果是执行成功。

       看一下源码:

       从源码中可以看出,判断锁的条件是os.getpid()(进程pid)与threading.get_ident()(线程标识符),如果每次acquire时的pid与ident都相同的时,即可成功。那么就可以在相同的进程中无限次数的acquire,但是多少次acquire就得多少次的release,防止死锁。

       那么是使用Lock还是RLock呢?这个要具体的看实际情况,并不是谁就一定好。

       总结本次推文中介绍了diskcache中FanoutCache缓存分片、双端队列deque、Index、Lock以及RLock。

       大家可以在实际中灵活运用,diskcache缓存的优势还是很大的,无需安装其他的模块,并且在文件管理器中能直接查看,还可以利用缓存的8元源码一些特性使用多线程的去实现业务等。

       但是也是有缺点的,即受制于本地文件系统的限制。每个磁盘每个目录下的文件数量是有限制的,所以需要结合实际情况使用。

OkHttp3源码详解之 okhttp连接池复用机制(一)

       提高网络性能优化,关键在于降低延迟和提升响应速度。

       在浏览器中发起请求时,header部分通常如下所示:

       keep-alive是指浏览器与服务端之间保持长连接,这种连接可以复用。在HTTP1.1中,它默认是开启的。

       连接复用为何能提高性能?通常,在发起http请求时,我们需要完成TCP的三次握手、传输数据,最后释放连接。三次握手的过程可以参考这里:TCP三次握手详解及释放连接过程。

       一次响应的过程:

       在高并发的请求连接情况下或同一客户端多次频繁的请求操作中,无限制地创建连接会导致性能低下。

       如果使用keep-alive,在timeout空闲时间内,连接不会关闭,相同的重复请求将复用原有的connection,减少握手的次数,大幅提高效率。

       并非keep-alive的timeout设置时间越长,性能就越好。长时间不关闭会导致过多的僵尸连接和泄露连接出现。

       那么,OkHttp3在客户端是如何实现类似keep-alive的机制的?

       连接池的类位于okhttp3.ConnectionPool。我们的目标是了解如何在timeout时间内复用connection,并有效地对其进行回收清理操作。

       其成员变量代码片段:

       excutor:线程池,用于检测闲置socket并进行清理。

       connections:connection缓存池。Deque是一个双端列表,支持在头尾插入元素,这里用作LIFO(后进先出)堆栈,多用于缓存数据。

       routeDatabase:用于记录连接失败的router。

       2.1 缓存操作:

       ConnectionPool提供对Deque进行操作的方法,包括put、get、connectionBecameIdle、evictAll等操作,分别对应放入连接、获取连接、移除连接、移除所有连接操作。

       2.2 连接池的清理和回收:

       在观察ConnectionPool的成员变量时,我们了解到一个Executor线程池用于清理闲置的连接。注释中这样解释:

       Background threads are used to cleanup expired connections

       我们在put新连接到队列时,会先执行清理闲置连接的线程。调用的正是executor.execute(cleanupRunnable);方法。观察cleanupRunnable:

       线程中不停调用Cleanup清理的动作并立即返回下次清理的间隔时间。继而进入wait等待之后释放锁,继续执行下一次的清理。所以可能理解成它是个监测时间并释放连接的后台线程。

       了解cleanup动作的过程。这里就是如何清理所谓闲置连接的流程。怎么找到闲置的连接是主要解决的问题。

       在遍历缓存列表的过程中,使用连接数目inUseConnectionCount和闲置连接数目idleConnectionCount的计数累加值都是通过pruneAndGetAllocationCount()是否大于0来控制的。那么很显然,pruneAndGetAllocationCount()方法就是用来识别对应连接是否闲置的。>0则不闲置,否则就是闲置的连接。

       进入观察:

       好了,原先存放在RealConnection中的allocations派上用场了。遍历StreamAllocation弱引用链表,移除为空的引用,遍历结束后返回链表中弱引用的数量。所以可以看出List>就是一个记录connection活跃情况的List。>0表示活跃,=0表示空闲。StreamAllocation在列表中的数量就是物理socket被引用的次数。

       解释:StreamAllocation被高层反复执行aquire与release。这两个函数在执行过程中其实是在一直在改变Connection中的List大小。

       搞定了查找闲置的connection操作,我们回到cleanup的操作。计算了inUseConnectionCount和idleConnectionCount之后,程序又根据闲置时间对connection进行了一个选择排序,选择排序的核心是:

       通过对比最大闲置时间选择排序可以方便地查找出闲置时间最长的一个connection。如此一来,我们就可以移除这个没用的connection了!

       总结:清理闲置连接的核心主要是引用计数器List>和选择排序算法以及excutor的清理线程池。

一文详解 ArrayDeque 双端队列使用及实现原理

       在探索Okhttp源码的奥秘时,一个不可或缺的组件便是ArrayDeque,一种强大的双端队列,它在数据进出两端提供了高效的操作。ArrayDeque作为Queue的扩展,拥有如offerFirst、offerLast、addFirst和addLast等一系列方法,允许在队列的两端进行元素的添加和移除,甚至可以设置为限制性操作,比如只允许一端操作。它的核心实现是基于数组,其中包含了head和tail这两个关键索引,它们控制着元素的进出。

       让我们深入剖析ArrayDeque的内部构造和关键接口:

       双端操作的魔法

       ArrayDeque的队列操作如诗如画,addFirst和offerFirst在队列前端插入,如E1、E2,而addLast和offerLast则在队列尾部,如Ea、Eb。head标识当前队首位置,tail则指向下一个待添加的位置,这种设计使得队列的增删操作既灵活又高效。

       初始容量与动态扩容

       ArrayDeque的构造器提供了多种选项,包括默认的8元素数组和自定义长度。默认构造会生成一个元素的数组,而自定义版本则通过allocateElements()函数找到大于所需长度的最小2的幂,确保足够的存储空间。例如,如果输入值是2^n,它会被提升到2^(n+1),而大于2^的值则设为2^,确保数组长度始终是2的幂次。

       首部操作的源码揭秘

       在核心操作中,offerFirst和addFirst的执行策略至关重要。offerFirst在数组末尾添加元素,若必要,会触发doubleCapacity()方法进行扩容。addFirst则避免了空指针问题,先在末尾添加,空间不足时才扩容。

       删除与出队

       pollFirst和removeFirst方法负责移除队首元素,遇到空队列时会抛出异常或返回null。同样,pollLast和removeLast用于移除队尾,同样具有类似的处理机制。

       尾部操作与数组扩容

       offerLast和addLast操作在数组前端向后添加,当队列满时,也会触发doubleCapacity()进行扩容,以保持性能。ArrayDeque的灵活性体现在不仅支持入队(offerLast)和出队(pollFirst)操作,类似地,push入堆栈和pop出堆栈也通过相同的逻辑进行。

       总的来说,ArrayDeque凭借其独特的设计和高效的实现,为Okhttp等应用提供了强大的数据管理能力。深入理解其工作原理,无疑有助于我们在编写高效代码时游刃有余。如果你对ArrayDeque的更多细节感兴趣,不妨参考官方文档或深入研究其在实际项目中的应用,如在Okhttp中的妙用。

LinkedBlockingQueue

        LinkedBlockingDeque在结构上有别于之前讲解过的阻塞队列,它不是Queue而是Deque,中文翻译成双端队列,双端队列指可以从任意一端入队或者出队元素的队列,实现了在队列头和队列尾的高效插入和移除

        LinkedBlockingDeque是链表实现的线程安全的无界的同时支持FIFO、LIFO的双端阻塞队列,可以回顾下之前的LinkedBlockingQueue阻塞队列特点,本质上是类似的,但是又有些不同:

        Queue和Deque的关系有点类似于单链表和双向链表,LinkedBlockingQueue和LinkedBlockingDeque的内部结点实现就是单链表和双向链表的区别,具体可参考源码。

        在第二点中可能有些人有些疑问,两个互斥锁和一个互斥锁的区别在哪里?我们可以考虑以下场景:

        A线程先进行入队操作,B线程随后进行出队操作,如果是LinkedBlockingQueue,A线程入队过程还未结束(已获得锁还未释放),B线程出队操作不会被阻塞等待(锁不同),如果是LinkedBlockingDeque则B线程会被阻塞等待(同一把锁)A线程完成操作才继续执行

        LinkedBlockingQueue一般的操作是获取一把锁就可以,但有些操作例如remove操作,则需要同时获取两把锁,之前的LinkedBlockingQueue讲解曾经说明过

        LinkedBlockingQueue 由于是单链表结构,只能一端操作,读只能在头,写只能在尾,因此两把锁效率更高。LinkedBlockingDeque 由于是双链表结构,两端头尾都能读写,因此只能用一把锁保证原子性。 当然效率也就更低

        ArrayBlockingQueue

        LinkedBlockingQueue

        问题,为什么ArrayBlockingQueue 不能用两把锁

        因为取出后,ArrayBlockingQueue 的元素需要向前移动。

        LinkedBlockingQueue内部由单链表实现,只能从head取元素,从tail添加元素。添加元素和获取元素都有独立的锁,也就是说LinkedBlockingQueue是读写分离的,读写操作可以并行执行。LinkedBlockingQueue采用可重入锁(ReentrantLock)来保证在并发情况下的线程安全。

        LinkedBlockingQueue一共有三个构造器,分别是无参构造器、可以指定容量的构造器、可以穿入一个容器的构造器。如果在创建实例的时候调用的是无参构造器,LinkedBlockingQueue的默认容量是Integer.MAX_VALUE,这样做很可能会导致队列还没有满,但是内存却已经满了的情况(内存溢出)。

        size()方法会遍历整个队列,时间复杂度为O(n),所以最好选用isEmtpy

        1.判断元素是否为null,为null抛出异常

        2.加锁(可中断锁)

        3.判断队列长度是否到达容量,如果到达一直等待

        4.如果没有队满,enqueue()在队尾加入元素

        5.队列长度加1,此时如果队列还没有满,调用signal唤醒其他堵塞队列

        1.加锁(依旧是ReentrantLock),注意这里的锁和写入是不同的两把锁

        2.判断队列是否为空,如果为空就一直等待

        3.通过dequeue方法取得数据

        3.取走元素后队列是否为空,如果不为空唤醒其他等待中的队列

        原理:在队尾插入一个元素, 如果队列没满,立即返回true; 如果队列满了,立即返回false。

        原理:如果没有元素,直接返回null;如果有元素,出队

        1、具体入队与出队的原理图:

        图中每一个节点前半部分表示封装的数据x,后边的表示指向的下一个引用。

        1.1、初始化

        初始化之后,初始化一个数据为null,且head和last节点都是这个节点。

        1.2、入队两个元素过后

        1.3、出队一个元素后

        表面上看,只是将头节点的next指针指向了要删除的x1.next,事实上这样我觉的就完全可以,但是jdk实际上是将原来的head节点删除了,而上边看到的这个head节点,正是刚刚出队的x1节点,只是其值被置空了。

        2、三种入队对比:

        3、三种出队对比: