- 博客(86)
- 收藏
- 关注
原创 ThreadPoolExecutor(九)——ScheduledThreadPoolExecutor和HashedWheelTimer比较
所以我们看到,我们大部分场景都不需要有纳秒级别的精确度,所以使用HashedWheelTimer可以让我们的处理速度更快,将超时时间不同但相差不大的多个任务在同一个tick内得到处理。因此,我倾向于使用硬件定时器来处理这种用例,因为您必须以最小的开销快速地安排任务,即O(1)用于新任务。来源(+ wheel定时器常规文档),以下是基本差异(N - 迄今为止所有未完成预定任务的数量,C轮大小): 的ScheduledThreadPoolExecutor。
2023-06-09 18:21:24
303
原创 ThreadPoolExecutor(八)——ScheduledThreadPoolExecutor
所以我们看到,我们大部分场景都不需要有纳秒级别的精确度,所以使用HashedWheelTimer可以让我们的处理速度更快,将超时时间不同但相差不大的多个任务在同一个tick内得到处理。因此,我倾向于使用硬件定时器来处理这种用例,因为您必须以最小的开销快速地安排任务,即O(1)用于新任务。来源(+wheel定时器常规文档),以下是基本差异(N-迄今为止所有未完成预定任务的数量,C轮大小)的ScheduledThreadPoolExecutor。...
2022-07-25 18:16:57
261
原创 log(五)——MDC总结
但是如果用ThreadPool线程池的话,线程是可以重用的,如果之前的线程的MDC内容没有清除掉的话,再次重线程池中获取到这个线程,会取出之前的数据(脏数据),会导致一些不可预期的错误,所以当前线程结束后一定要清掉。所以MDC类的静态变量mdcAdapter(LogbackMDCAdapter实现)中的copyOnInheritThreadLocal会得到父类MDC写入的内容,因为它用的是。答如果都是用newThread方法建立的线程没有问题,因为之后线程会消亡。......
2022-07-23 00:10:09
1512
原创 log(四)——MDC使用
之前说过,子线程在创建的时候会把父线程中的inheritableThreadLocals变量设置到子线程的inheritableThreadLocals中,而MDC内部是用InheritableThreadLocal实现的,所以自然会把父线程中的上下文带到子线程中。对于线程池中的线程来说,这部分线程是可以重用的,但是线程本身只会初始化一次,所以之后重用线程的时候,就不会进行初始化操作了,也就不会有上一段中提到的父线程inheritableThreadLocals拷贝到子线程中的过程了。......
2022-07-23 00:09:55
1971
原创 log(三)——MDCAdapter之实现LogbackMDCAdapter
然后这个方法中的同步块也很奇怪,为什么父线程会能修改这个map,其他线程能修改这个map的地方只有通过getPropertyMap方法来获得copyOnInheritThreadLocal内容的引用,然后修改这个map的内容,只有这一处,但是一般使用MDC应该不会用getPropertyMap方法获取内容然后进行操作吧,不知道为什么把这个方法公有化,而不是包一个ImmutableMap再对外暴露。这里觉得copyonwrite依然浪费性能,所以只在get操作后做copyonwrite。......
2022-07-23 00:09:44
856
原创 log(二)——MDC实现之ThreadLocal
在Thread的init方法中,是通过获得当前线程作为parent线程,也就是说,在哪个线程中new的这个Thread并start的,执行该操作的线程就是new的新Thread的parent线程。threadLocals保存的是当前线程中的ThreadLocal变量们,inheritableThreadLocals保存的是当前线程父线程中的变量们。但是init方法只在线程初始化的时候执行一次,所以如果用的线程池来使线程重用的话,就不会再调用这个init方法了,这会带来一些问题,后面会具体说。......
2022-07-23 00:09:31
699
原创 log(一)——MDC入门
MDC中包装了一个MDCAdapter,这是一个接口,不同的log包有自己对应的实现。对于不支持MDC的包,比如java.util.logging,使用默认的BasicMDCAdapter,slf4j-nop有NOPMDCAdapter。简单来说就是日志的增强功能,如果配置了MDC,并添加了相应的keyvalue,就会在打日志的时候把key对应的value打印出来。内部是用ThreadLocal来实现的,可以携带当前线程的context信息。这是MDC类上的注释,但是没有讲这个是干什么的。......
2022-07-23 00:09:21
974
原创 ThreadPoolExecutor(七)——总结&补充
线程池的submit方法提交的任务是一个FutureTask,它既含有Runnable的任务属性也含有Future的属性。在设置异常之后,用future的get(内部是Sync的innerGet方法)方法获取结果的时候会抛出异常,线程池在执行execute方法和submit任务的时候,如果任务中抛出了异常,外部线程无法感知,所以要做一些措施来进行处理,下面就说一下产生这种效果的原因以及一些可用的处理方法。在构造线程池的时候,作为参数传递我们自己封装的线程工厂。,同时记录日志和监控。自己封装线程的工厂类。..
2022-07-23 00:09:06
224
原创 ThreadPoolExecutor(六)——线程池关闭之后
上一篇主要从代码角度介绍了线程池关闭相关的方法,包括各个方法之间的逻辑关系,调用关系和产生的效果。这一篇更多从逻辑角度上来说一下线程池在shutdown之后,原来正常的处理流程有哪些变化,既是总结也是扩展。shutdown操作之后,首先最重要的一点变化就是线程池状态变成了SHUTDOWN。该状态是开始关闭线程池之后,从RUNNING改变状态经过的第一个状态(还有一种情况是直接进STOP,调用shutdownNow的时候),有个图画的挺好,见博客。...
2022-07-22 18:04:55
2199
原创 ThreadPoolExecutor(五)——线程池关闭相关操作
补充了和Thread的interrupt操作相关的知识,回头再来看ThreadPoolExecutor中interrupt,关闭线程池等相关操作。先看注释:开始一个顺序的shutdown操作,shutdown之前被执行的已提交任务,新的任务不会再被接收了。如果线程池已经被shutdown了,该方法的调用没有其他任何效果了。该方法不会等待之前已经提交的任务执行完毕,awaitTermination方法才有这个效果。具体看内部逻辑,checkShutdownAccess这个方法是确保允许调用发interr
2022-07-22 18:04:23
2457
原创 ThreadPoolExecutor(四)——Interrupt
Thread.interrupted()方法判断的时候返回了true,同时擦除了中断标志位,所以再用Thread.currentThread().isInterrupted()判断的时候,就返回false了。Thread的成员函数isInterrupted判断标志位的时候不会清除标志位,如果返回true,那么无论执行多少次都会返回true(期间没有其他清除标志位的操作的话)。interrupt对于正常运行的线程起不到中断作用,只是把该线程的中断标志位设置为true了,线程会继续正常运行。...
2022-07-22 18:03:45
640
原创 ThreadPoolExecutor(三)——Worker
我们可以在beforeExecute方法中抛出异常,这样task不会被执行,而且在跳出该循环的时候completedAbruptly的值是true,表示theworkerdiedduetouserexception,会用decrementWorkerCount调整wc。上面说过,addWorker会用当前task创建一个Worker对象,相当于对task的包装,然后用Worker对象作为task创建一个Thread,该Thread保存在Worker的thread成员变量中。...
2022-07-22 18:03:15
411
原创 ThreadPoolExecutor(二)——execute
3.如果任务无法入队列,再次尝试addWorker,这次是用正在run的线程数和maximumPoolSize比,如果超过了maximumPoolSize则reject任务,说明线程池已经饱和了。2.如果addWorker方法执行失败了,任务要入队列,如果成功入队列了,需要做doublecheck来处理一些极端情况,比如线程池是否shutdown了等等。这个代码块,首先加锁,整个类用到这个锁的地方,除了获取该线程池的一些关键参数之外,就是shutdown和terminate等相关操作。...
2022-07-22 18:02:42
639
原创 ThreadPoolExecutor(一)——简介
一个ThreadPoolExecutor从初始化到执行需要经历如下几个环节。ThreadPoolExecutor初始化。
2022-07-22 18:02:12
179
原创 pool(六)——JedisPool
和上一篇数据库连接池的PoolingDataSource一样,这个Pool类包装了对象池,里面存放了一个GenericObjectPool类型的internalPool变量。本文剖析redis.clients.jedis中关于jedis连接池部分的源码。因为只涉及连接池的部分思想,不设计具体jedis底层创建连接部分,所以篇幅很小。和jedis的创建销毁相关的工厂类。...
2022-07-22 18:00:21
135
原创 pool(五)——BasicDataSource
也是从_pool这个变量中调用borrowObject拿对象,这个_pool对象就是上面说的GenericObjectPool,所以最终还是调用GenericObjectPool的borrowObject方法拿对象。可以把PoolingDataSource看作GenericObjectPool的包装,BasicDataSource的getConnection最终还是从GenericObjectPool中拿对象。query真正执行的时候,才会去获取连接。1.一次query流程。......
2022-07-22 17:59:43
354
原创 pool(四)——EvictionTimer
但是如果{@linkGenericObjectPool#createCount}=={@linkGenericObjectPool#maxTotal},{@linkGenericObjectPool#create}方法就无法继续创建对象了。该类是TimerTask的子类,run方法有两个主要功能,evict和ensureMinIdle,一个是清除对象池中过期的对象,一个是保证对象池中始终有个数为minIdle个对象。该方法中使用了EvictionPolicy来判断是对象池中的对象是否过期了。......
2022-07-22 17:59:12
288
原创 pool(三)——Timer
第三个入队列,index是3,3>>1=1,和第一个比,queue[3]比queue[1]要早,所以交换顺序,所以现在queue[1]最早执行,queue[2]和queue[3]的顺序没有考虑。这里以3个为例,第一个入队列,index是1,第二个入队列,index是2,2>>1=1,然后拿queue[2]和queue[1]比较下次执行时间,queue[2]比queue[1]早,所以交换顺序。所以,当本次任务执行结束(过了超过5s),到下次任务取出来判断的执行时间的时候,肯定已经超过了原本应该执行的时间。..
2022-07-22 17:58:39
114
原创 pool(二)——动手入门
保证{@linkGenericObjectPool#idleObjects}对象的size和{@linkGenericObjectPool#getMinIdle}一致。但是如果{@linkGenericObjectPool#createCount}=={@linkGenericObjectPool#maxTotal}池化对象工程,这里的池化对象是MyConnection,这个工厂就是负责产生MyConnection对象的,产生的对象会放到池子中。4.对象池中对象数量控制。......
2022-07-22 17:58:06
131
原创 pool(一)——入门
线程池的用法和这个不太一样,因为线程的run方法执行完之后,一个线程的使命就结束了。所以如果要想重用线程,就需要阻止run方法的结束,用死循环等方法。目前jdk线程池使用的是结合LinkedBlockingQueue,通过队列的阻塞方法让当前线程休眠,等待任务并处理,实现单个线程的重用。取任务的时候,如果allowCoreThreadTimeOut是true,或者当前线程数量已经超过了核心线程数量,从LinkedBlockingQueue中取任务会有等待时间,等待的时间由keepAliveTime决定。..
2022-07-22 17:57:34
152
原创 Disruptor 1 内部运转流程
可以调用Disruptor的构造函数初始化,可以设定线程池,等待策略,ringBuffer的大小等信息。注意,在高版本的Disruptor中,带线程池的构造方法已经被打上了Deprecated标记,原文是这样解释的:使用ThreadFactory来代替Executor的原因是为了能够在无法继续创建线程来执行producer任务的时候能够报告errors。其实关于这一点个人持保留意见,因为这样封死了传入Executor的可能性,但是Disruptor内部又只有一个BasicExecutor的封装,这个线程池的
2022-07-22 17:27:46
182
原创 Disruptor 0.1 相关知识及预热
这个测试程序调用了一个函数,该函数会对一个64位的计数器循环自增5亿次。如果增加一个锁(仍是单线程、没有竞争、仅仅增加锁),程序需要耗时10000ms,慢了两个数量级。更令人吃惊的是,如果增加一个线程(简单从逻辑上想,应该比单线程加锁快一倍),耗时224000ms。但是其实现在已经有改观了,synchronized关键字的底层实现引入了偏向锁和轻量级锁(自旋),所以性能没有之前那么差,这些可以在上面的文章中找到答案。在Disruptor为什么快的问题上,博客里指出了一个实验,即。...
2022-07-22 17:27:36
60
原创 java特种兵读书笔记(5-2)——并发之线程安全
线程安全与处理器核数多核处理器会有可见性问题引发的线程安全问题。单核处理器不会有这种问题,但是也会有一致性问题,因为CPU有时间片原则,还会重排序等等。并发内存模型为了提升性能,CPU会cache许多数据。在多核CPU下,每个CPU都有自己独立的cache,每个CPU都可以修改同一个单元数据,有自己的一套缓存一致性协议——从CPU的角度认为某些来自主存的数据的读取和修改需要一定的一致性保证。JVM中,每个线程的私有栈在使用共享数据时,会先将共享数据拷贝到栈顶进行运算,这个数据是一个副本,所以存在多个线程修改
2022-07-22 16:49:05
148
原创 Netty第二章 2020 7-4——Netty在Dubbo中的应用(3)关于单一长连接
dubbo默认是单一长连接,那么这个默认在哪里,长连接又是什么意思,单一长连接又是什么意思呢?首先dubbo底层用的是netty,所以长连接说的也是通过netty创建的连接,本文主要说的是dubbo中单一长连接的概念和理解,关于netty不做更多展开。话不多说,直接上源码:DubboProtocol在refer的时候,也就是consumer端启动去发现服务的时候,会调用getClients方法该方法会检查xml中配置的一个叫做connections的参数,如果用户没有配置,那么缺省配置就是0,0代表共享连接
2022-07-11 22:19:33
386
1
原创 Netty第二章 2020 7-2——Netty在Dubbo中的应用(1)Consumer端
这篇里主要说下dubbo中和netty通信相关的源码部分,consumer端主要的几个流程包括,发起连接connect,发起refer,发起请求request,接收响应,断开连接等等。AbstractPeerconnect过程:DubboInvoker中的Exchange currentClientDubboProtocol中getClients,initClient会Exchangers的connectExchanger(HeaderExchanger)的connect,该方法会返回一个HeaderExc
2022-07-11 22:17:32
180
原创 Netty第二章 2020 3-9 Netty源码之flush优化
Netty的实现更复杂一些,对于用单独线程池处理业务的场景,有一些特殊的处理和优化,比如如果异步处理的时候,即channelReadComplete比channelRead结束的要早,所以在flush调用的时候,readInProgress已经是false了,然后根据用户决定是否开启对于异步的强化来决定是直接flush还是走consolidateWhenNoReadInProgress,如果consolidateWhenNoReadInProgress为true(开启增强),那么就用次数是否到达阈值来决定立
2022-07-11 22:16:13
552
原创 Netty第二章 2020 3-8——Netty源码之Context,Channel,Pipeline,EventLoop,Handler,Selector
在看Netty源码的时候,我们经常会看到Context,Channel,Pipeline,EventLoop,Handler,Selector这些东西,尤其在debug的时候,经常会被这些概念弄得晕头转向,比方说:pipeline中有context,context中又有handler,context中又有channel,channel中又有pipeline,NioEventLoop中有selector,一个selector管理多个channel,各种彼此拥有彼此的关系,很乱。这里我们就简单梳理一下这些对象,
2022-07-11 22:15:57
158
原创 Netty第二章 2020 5-1——实战篇之Reactor模型的线程解析
条件:①构造EventLoopGroup的时候,boss组和worker组大小都设置为1。②在ServerHandler中添加sleep这里是Worker的NioEventLoop线程执行的回调。然后在client端调用的时候会阻塞③代码逻辑服务端接收请求的时候返回当前时间戳返回给客户端,同时模拟繁忙业务处理,sleep了10s才返回时间戳给client端。异步启动两个客户端并向server端发起连接请求。结果:Server端日志:17:24:17.084 [TimeServer-worker-3-1
2022-07-10 22:00:02
113
原创 Netty第二章 2020 3-3——Netty源码之Server端Worker线程组的启动
Server端会接收客户端发起的连接操作,然后启动worker线程组。上文(Netty第二章 2020 3-2——Netty源码之Server端Boss线程组的启动_普通网友的博客-优快云博客)提到了,NioEventLoop中有两个重要的操作,分别是NioEventLoop.processSelectedKeys和SingleThreadEventExecutor.runAllTasks。说完了runAllTasks,下面说下processSelectedKeys。当client端发起连接的时候:Bos
2022-07-10 21:56:00
285
原创 Netty第二章 2020 3-2——Netty源码之Server端Boss线程组的启动
以主从Reactor为例,Server在启动的时候,会创建Boss和Worker两个EventLoopGroup,这两个group会作为参数传入到ServerBootstrap的group方法中。AbsttractBootstrap.bind->绑定端口AbsttractBootstrap.initAndRegister->初始化channel并注册channelMultithreadEventLoopGroup.registerSingleThreadEventLoop.registerAbstractC
2022-07-10 21:55:34
304
原创 Netty第二章 2020 3-1——Netty源码之Reactor模型部分
关于Reactor有一些地方想做下说明,因为看到网上说到这块内容的文章不是特别多,而提到这块的文章的一些用词可能会对一些同对Netty不是特别了解,或者正在学习Netty源码的同学造成一定困扰和误解,所以想在这里分享出来。这里有些理解可能和一些同学平时的认知不太相同。如果本文所写有任何问题或者错误,欢迎指出讨论。NioEventLoopGroup有多个构造方法,也有无参构造方法。无参构造方法将nThreads设置为0。0就根据CPU核数决定入参nThreads。如果不是0,就用你传入的nThreads来决定
2022-07-10 21:55:07
56
原创 Netty第一章 1-2 Netty权威指南读书笔记(二)
1.CS模型两个进程间的交互。服务端:提供位置信息(绑定的IP地址+监听端口)。客户端:通过连接操作像服务端监听的地址发起请求。通过三次握手建立连接,成功后就可以通过socket进行通信了。2.同步阻塞模型ServerSocket:负责绑定IPSocket:发起连接操作连接成功后,双方通过输入输出流进行同步阻塞式通信。3.BIO通信模型(一请求一应答模型)服务端:一个独立的acceptor线程负责监听客户端的连接。每接收到一个客户端的连接请求,就为其创建一个新的线程进行链路处理,处理完毕通过输出流返回应答给
2022-07-10 21:52:36
126
原创 Redisson(4)分布式锁之RedLock
Redis的官方文档对用Redis分布式锁的难点以及解决方案的考虑做了一些说明,具体内容参见:/docs/reference/patterns/distributed-locks/,译文参考:《Redis官方文档》用Redis构建分布式锁 | 并发编程网 – ifeve.com场景一:保存锁的Redis的master节点挂了。这里不是说有slave就没问题了,因为Redis的数据复制功能(replication)是异步的,存在这样的可能,线程1获取到锁了,该锁在从master复制到slave之前,maste
2022-07-10 21:50:42
406
原创 Redisson(3)CountDownLatch实现对比 VS Java的CountDownLatch
和锁类似,Java实现CountDownLatch的时候也利用了一个叫做AQS的东西,源码参照java.util.concurrent.locks.AbstractQueuedSynchronizer。这个类的其中一个思想核心是内部的一个state状态变量,这个状态会告诉你当前的锁是否可用,当前的latch是否倒计时结束等等。核心接口连个方法,一个是countDown,另一个是await,countDown是改变状态的,await可以类比对锁的等待。先看countDown这里使用自旋的方式,原子性改变状态,
2022-07-10 21:50:31
507
原创 Redisson(2-3)分布式锁实现对比 VS Java的ReentrantLock的FairLock
Redisson实现了一整套JDK中ReentrantLock的功能,这里对比一下公平锁(Fair)实现的差异和核心的思想。公平锁存在的意义是为了保证绝对的公平,但是有其弊端,这个在网上有很多相关的解释,就是说绝对的公平不一定是性能最高的,因此和它相对的还有非公平锁,但是费公平锁也有问题,会引发饥饿现象。具体解释也可参见AbstractQueuedSynchronizer(九)——ReentrantLock公平和非公平锁_普通网友的博客-优快云博客fair模式的带超时时间的tryLock(超时时间)Ree
2022-07-10 21:49:52
467
原创 Redisson(2-2)分布式锁实现对比 VS Java的ReentrantLock之带超时时间的tryLock
Redisson实现了一整套JDK中ReentrantLock的功能,这里对比一下实现的差异和核心的思想。unfair模式的带超时时间的tryLock(超时时间)ReentrantLock这里上来会直接先试下能不能try成功,如果不成功,进入等待并开始竞争等逻辑。整个锁的核心是通过LockSupport的park方法来实现的,这是调用底层UNSAFE的park方法来实现的。如果在被等待获取的锁释放的时候,该线程会重新被唤醒,然后和其它线程一起竞争,如果没有竞争成功,那么继续park,循环往复,直到获取到锁或
2022-07-10 21:49:25
905
原创 Redisson(2-1)分布式锁实现对比 VS Java的ReentrantLock之tryLock
Redisson实现了一整套JDK中ReentrantLock的功能,这里对比一下实现的差异和核心的思想。unfair模式的tryLockReentrantLock①判断当前的state是否是0(初始状态),并用原子操作设置state,成功说明获取锁,并把当前线程设置为获取锁的线程。②如果state不是0,检查当前线程是否是持有锁的线程,如果是就按照重入语义,增加计数,当然前提是不能超过最多重入的次数(不能溢出),然后将最终计数设置到state中。③如果既不是①也不是②,那么tryLock失败。Rediss
2022-07-10 21:49:15
391
原创 Redisson(1)分布式锁——如何解决死锁问题
Redisson是如何解决死锁问题的?普通利用Redis实现分布式锁的时候,我们可能会为某个锁指定某个key,当线程获取锁并执行完业务逻辑代码的时候,将该锁对应的key删除掉来释放锁。lock->set(key),成功->执行业务,业务执行完毕->unlock->del(key)。根据这种操作和实践方式,我们可以分为下面两个场景:1)业务机器宕机因为我们的业务不知道要执行多久才能结束,所以这个key我们一般不会设置过期时间。这样如果在执行业务的过程中,业务机器宕机,unlock操作不会执行,所以这个锁不会被
2022-07-07 00:12:53
4482
原创 Java并发编程实践笔记(二)——chapter1(线程安全)
多个线程操作一个全局变量int i,操作是i++。通过volatile关键字是解决线程安全问题的必要条件。保证原子性也是解决线程安全问题的必要条件。因为i++不是原子操作,读取值,加一和赋值操作是分开的,所以有可能在读取值之后(读取到当前线程副本中),i值(主线程中的本尊)就发生了变化,也有可能i在被赋值之前发生不可预期的变化。servlet是无状态的,但是如果需要用无状态的servlet去记录一个状态(比如请求次数),这个时候要么使用同步,要么使用原子变量。“编写线程安全的代码,本质上就是管理对状态的访问
2022-07-07 00:10:05
74
原创 java特种兵读书笔记(6-2)——数据库之主从
主从同步与备份一套主从库通常会有一个可写的主库(也有双主互备结构的),可写的主库发生数据修改之后,可能会以推的方式发送到从库,或者从库轮询从主库上拉修改的数据。① 逻辑模式基于主库执行的SQL,到从库上去执行相同的SQL,也可能基于主库上执行的SQL时所影响的数据行,进一步根据这些行的主键生成一些列SQL语句到从库上执行。这种粒度最细,最容易识别,但是生成的SQL会比较多。如果以SQL去执行,从库肯定会有一定的压力,但是这种从库的写通常是单线程的。不论主库有再高的并发压力,从库只有一个线程在发生写操作,不断
2022-07-07 00:09:41
243
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人