一:svnphp
1. svn linux中的基础。前端
2. svn有钩子脚本,支持提交先后的验证,支持自动同步,格式验证和控制等。java
3. 小型公司运维发布方案:a,临时目录传完mv过去或link过去,时间很是短。B.通常是平滑下线。再更新,再提上。node
4. 大型公司部署方案linux
平滑下线,流量低谷下线一半服务器保证不会瘫痪,而后挂到一个内部的测试lvs上测试。通常分2批上线,3组可能有明显变化。特色是全部的修改都通过svn的流程分发(避免异常修改,同时能记录修改状况)。c++
5. php发布确定是不推荐直接往上推,用mv,link的方式,php不重启,相对java简单点。程序员
6. 有的门户网站前端有DNS智能解析,能够分地区的给用户访问。能够控制影响范围。ajax
7. 通常越是前端更新频率越高,越是后端更新频率越低。算法
8. 发布代码如何保证不影响用户,如何回滚。spring
9. 软件版本尽可能单一,要么都用一遍,辞职了,麻烦,是为了控制成本。
10. IDC测试阶段就要压力测试,能程序测试的就程序测,但程序僵硬的地方仍是人测。
11. 流量低谷下线过程通常经过脚本平滑下线。平滑的意思是提供服务的提供完再关闭,新来的就拒绝了(超市买东西)。
12. 一个严格的公司必定要文档,要么离职会要挟你,要挟不了。
13. 大公司网站资源和程序通常是分离的,图片视屏,上线尽可能是全量上线就以svn为准,要保证svn代码是最新的。
14. 全部的配置文件都放svn上,保证运维的稳定。
15. 大公司通常设置配置管理的开发与运维的中间纽带,她控制svn管理,上线管理,流程申请,业务协调等工做。
16. 通常大公司或注意流程或开发能力强的公司会本身实现一个自动部署的平台。
17. 开发运维业务变动用管理平台处理,隔离具体的人,和保留变动历史核查问题。
18. 领导是想的多,架构是说的多,作的多。运维,程序员是作的多。
19. 没有架构师的参与,由程序员本身搞的项目并发撑不住多少。用申请的方式能够打掉一些不是必要的事情,或能够批量申请。流程有限制也有它的好处。
20. 你本身搞的东西,就算再正确,推行阻力也大,而首先是大多数承认你是前提,不然实施也有可能跑偏。因此要注意团结群众。不然在萌芽中就死掉了。
21. 淘宝门户每次build一个包,就有一个新版本,svn也是会打个tag,预发,到集群,批量发布减小用户影响的范围,实在出问题也只能回滚了,就实现了版本管理。
22. 灰度发布,分地区,分批,时间还比较长,因此效率比较低,通常核心流程,淘宝才走这种流程。好比说交易的代码变动要求走灰度。
23. 上线须要知晓产品,运营,市场,开发,运维,紧急上线可能须要CTO签字了。
运维产品更新有套流程,不是说更新完了就走人了,要么老大都不知道你在干啥。
24. 越往上走应该更重视流程和制度,而不只仅是技术了,技术会的再多,也不如领导一句话工资高。
总结:
1. 有一点说的挺对的,通常从svn本地进行编译打包,而后统一推送到分发服务器。从架构上确保编译时的正确性。
二:java的并行
第一周:
1. 为何要并行,单线程是能够的,只是有些调度问题,比较复杂。
2. Jvm中有grt gc main一些线程不可是串行并且有交叉,对调度来讲就交给操做系统,业务的逻辑单元编码就须要线程,进程开销又太大。(业务须要)
3. Linus Torvalds以为图像处理(计算密集型)和服务端编程(数据量大,须要大量的数据)。(多核上确实是知道的,并且java比较适合的领域)
4. 并行是由于多核,多核由于摩尔定律失效,cpu主频瓶颈到了,没有选择走到多核。
5. 并行是同时作(多cpu),并发是间歇的切换不一样事情作(单cpu)。
6. 临界区是共享资源,可被多个线程使用,但每一次,只能有一个线程使用,一旦临界区被占用,其余线程想使用这个资源,就必须等待。(避免冲突,须要控制,有点像加锁的感受)。
7. 阻塞(好比一线程占用了临界区资源,那么其余所须要这个资源的线程就必须在这个临界区中进行等待,等待会致使线程挂起,这就是阻塞。若是占用线程一直不释放资源,那其余全部阻塞在这个临界区上的线程都不能工做。)
操做系统层面上下文切换须要8万个时钟周期,效率并不高。
8. 非阻塞(容许多个线程同时进入)
9. 死锁,全部线程卡死,是个静态问题,cpu=0。
10. 活锁,电梯遇人,A,B都释放,而后又获取,而后又释放,又获取,很不容易发现,并且占用资源。
11. 饥饿,多是优先级问题分不到资源,或竞争数据总失败不能往下走。
12.分阻塞/非阻塞
12. 无障碍是最弱的阻塞调度,一个线程进临界区并不要求其余线程等待。阻塞调度认为它是一种悲观策略,它认为你们一块儿修改数据可能把数据改坏,它本身认为是乐观的(宽进,严出,若是有竞争会回滚)。
13. 无锁,保证至少有一个线程能出去。保证能够顺畅执行下去。
14. 无等待,首选能进能出,若干步就能运行完成,无饥饿的,效率是比较高的。
若是有写,每次写前copy副本,而后修改副本不影响读,并且写也不用同步,只是在最后阶段覆盖原始数据是很是快的,无论哪一个胜出,替换是一致的。数据仍是安全的。
15. Amdahl定律,就是优化先后的比率。加速比=优化前系统耗时/优化后系统耗时。
有个公式 n个处理器的加速比T1(F+(1-F)/n)=Tn,这个公式说明并行程序要多并且处理器要多时可能会提升加速比,可是有个均衡的比例,这个可能最小投入获得最大加速比。
意思是说,若是整体是串行的,总体加不少cpu,用处不大。说明单纯增长cpu并不能提高加速比。
16. Gustafson定律 S(n)=n-F(n-1),F是线性比例,n=cpu个数,若是串行化比例足够小,并行比例足够大的话基本和cpu个数成正比。因此并行程序比例越大,cpu对加速的做用更明显。若是串行化比率必定的话,提高cpu个数理论能提升串行化比率(这个估计得限制一条,有能够进行并行化执行的程序)
第二周:
1. 进程里有不少线程,每一个进程的切换是很重量级的。用进程并发是不会过高的。
2. Java中的线程会映射到操做系统中的线程,基本上是等价的(这个之前还不太肯定的理解)。
3. 线程start-》runnable状态说线程准备好了准备执行了,但并不必定确实在cpu上执行,只是在java层面说我各类锁资源准备好多了,实体cpu未必分出时间片来给他执行,那么到底执行没执行就看cpu调度状况了。
4. 当线程准备进入临界区要执行,可是别的线程占用致使没有得到这把锁,就会进入到blocked被挂起的阻塞状态,好比(synchornized)。
5. 线程执行过程当中若是调用了wait进入等待状态,那么就会等待别的线程来进行notify他,若是通知到了,那就又会进入runnable状态。有限的等待叫TIMED_WAITING。
6. Start()是开启一个线程,在新的操做系统线程上调用run()方法,最后掉的都是run()。
不过直接调用run(),只是在当前调用线程执行,而不能开启一个线程。
7. run()的实现,
8. stop()比较暴力,并不知道线程具体执行那个步骤,会释放掉全部monitor。以下图:
为了保持数据的一致性,对某个对象读写lock,当u1更新id后,执行stop,在name更新前就结束了线程并释放了lock,u2等待锁得到所后进行读取id,name就可能不一致了(由于u1的name尚未更新)。因此很是规状况,sun不建议使用这个方法。
9. 线程中断Thread.interrupt()就是给线程打了个招呼,它会把中断标志位给置上,有人更我打招呼后我可能就会处理某些事情,比stop这种暴力的方式。对于那些内有大的循环体的方法咱们是才可能有意愿去把他stop掉的,几步能作完的线程没有必要去stop。
用下面这种通知方法去中断线程的好处是不影响正在执行的业务。
Sleep为何要try catch呢?若是有人须要中断个人sleep,那么是容许中断处理的,可是抛出了interrupt异常interrupt状态会被清空,因此异常处理中还要再次调用interrupt。
10. suspend()获得lock后,临界资源是被suspend,临界资源并不会被释放,所以没有任何线程能够访问被锁住的资源,直到调用了resume方法,但若是resume提早调用了,还会冻结。像下面这种状况th1的线程被提早resume,致使没有其余线程能够再次resume,th1在resume后被suspend就会永久的挂在那里。
11. 一个好的线程的名字很重要,出问题的时候能够在错综复杂的信息中尽快找到问题。
12. Yield(),我但愿其余线程有机会增多cpu,我释放掉当前占用的cpu,但没有放弃竞争机会。
13. Join(),咱们只是开启了一个线程,由于是异步的,并不知道线程是否执行完毕,可是我又迫切的须要知道你是否执行完毕,我在迫切的等待你执行的一些数据,我会等你结束再来作事情,我要等下你,等一下子我们再一块儿走。
14. Join()的本质是wait(),那么那个线程执行完毕后会调用notifyAll去通知等在上面的线程。NotifyAll这个方法的调用实际是JVM中的c++去调用。所以不建议在线程实例上调永wait,notify,notifyall方法。
15. 守护线程,默默后台运行,和业务关系没那么大,起些赋值性做用,为整个系统提供支撑服务。非守护进程结束,虚拟就就会退出,守护线程不做为虚拟机退出的一个标志。
16. 线程优先级,通常俩说高优先级的线程更可能竞争到执行资源,但不绝对。
17. 线程同步,若是一个线程被挂起被等待了,那如何通知呢,若是咱们有资源竞争该怎么协调这个资源呢。Synchornized是个JVM内部自我实现的拿锁,挂起,优化,自旋,拿到对象的锁或监视器。
Synchornized,
a.对象锁,要得到给定对象的锁。
B.方法锁,对象实例的锁(同对象生效)。
C.静态方法,须要得到class锁(类通常都是生效的)。
18.
Object.wait()这个对象执行线程等待,前提是必需要先得到执行的对象的监视权(lock)才能执行并且同时wait也必须释放这个对象的全部权,要么其余对象就不能执行。
Object.notify(),也是须要得到object’mointor,并且只唤醒一个等待的这个对象实例。
Notify()后,wait的线程是被容许继续执行了,但要执行前也必需要得到这个监视器才行。
19.notifyall(),这个图仍是挺形象的,让全部线程去争用监视资源。
20.原子是一个不可中断的,即便多线程一块儿执行,一旦开始接不被其余线程干扰。
i++(r,u,w)并非一个原子的操做。有一点读32位的long不是原子性操做,读int是一个原子性操做。
21.有序性,并发时,程序的执行可能会出现乱序。好比这两个线程中的方法,writer时执行的顺序多是flag=true在线。线程B读取时可能先看到的是flag=true,而a的值未必是1。
22.一条指令的执行会分不少步骤的(机器码-)汇编指令)指令的执行和硬件有关系,不会一组组的一条条的顺序执行完才行行下一组,而极可能是硬件指令排序执行的。
好比一套指令:if,id,ex,mem,wb这几个阶段。下面这个计算过程实际上能看出执行时cpu的竞争过程。x的过程,有的是数据没有到达,没办法作,有的是硬件竞争,空出一个时钟周期。指令重排可让执行流程更加顺畅,运行周期更短。(可是原则上是执行没有改变语义的执行可能指令重排,重排只是编译器,cpu优化的一种方式)
23.可见性问题,
a.编译器优化:一个变量在寄存器,一个在高速缓存,对于每一个独立的cpu有本身一套的cpu,对于同个变量不可能互相都知道必定一致。
b.硬件优化。(写到硬件队列中,批量操做)
cpu之间有一些数据一致同步的协议,若是没有可见性问题,那可能性能会变的不好。
25. 可见性问题在别的线程多是看不见的。
大爷的编译重排是汇编层面的,确定又是一些优化规则或算法?
总之多线程总会出现数据间优化替换的问题。
26. happen-before先行发生规则
27. 线程安全
线程执行时是安全的,好比i++操做就是不安全的,一个语句是可能分多个汇编指令的操做的,并且有的会出现优化和重排。
三:无锁的算法
1. 无锁是无障碍的全部线程都能进入临界区,可是必须有一个线程能胜出。
A1.compareAndSet,看某值的偏移量是多少,进行对比
A2.getAndIncrement,通常的尝试是放一个循环体里,不断尝试是否成功,这个指望值判断算法是个方法。
for(;;){
int current = get();
int next = current;
if(compareAndSet(curretn,next)){
return current;
}
}
B.unsafe的偏移量是相对于class的字段的偏移量。Unsafe的一些操做是关于偏移量和volatile的。一些高性能并发的框架也会使用unsavf类。
C.atomicreference为了保证引用修改的安全可使用这种方式修改。
d.atomicstampedreference,有惟一性标志的字段,没有重复的递增数据。和过程状态相关的,和结果无关的,若是用普通的cas操做有的就不能明确区分了。那如何区分呢,那就是加入时间维度。能够在过程敏感的方法中来区分这样的问题。
E.atomicIntegerArray,接口多个下标参数。咱们会看到jdk和并发的运算内部有挺多的位运算,位运算比传统运算性能要好一点点。
F.atomicIntegerFieldUpdater,整数的字段更新,可以让普通变量也能实现原子更新。好比有的成员变量但愿使用CAS操做,可是又不想修改数据类型。
G.atomicreferenceArrayReferenceArray把1维数组作成二维数组的样子。对于保证性能的并发操做郁闷的就是修改数据,若是把一部份内容固定下来,尽量少的去修改原来的元素,由于同步是困难的。这个内部有不少basket,能够尝试修改内部某个一味数组的某个位置的值,能够重试,失败就失败了。这些内部的JDK算法仍是有些复杂的(这哥们果真牛呀)。毕竟性能的提高是个系统的工程。调度分配,元素都不少。
四:并发1
JDK并发包1
1. ReentrantLock是synchornized的一个替代品,性能上jdk5之后差的很少了。
2. condition相似于object的wait(await)和notify(signal),也须要先得到锁才能通知,也是先得到锁后才能继续执行。
3. semaphore是一个共享锁,运行多个线程,按系统的能力去分配许可,许可内就执行,超过了许可不可了就等待。固然这些许但是能够按需分配的。好比锁是个信号量为1的信号量。信号量的使用实际也是一种对资源的分配。
4. readwritelock,synchonize,reentrantlock不分读写,是有阻塞的并行。从功能上进行划分,可让并行度加多,这个若是都是read的就可能作到无等待的并发,高并发有可能。(读读不互斥,读写互斥,谢谢互斥)却是能够看看性能影响多大。
5. countdownlatch,好比火箭的发射每项的检查都由一个线程来执行。 每一个执行完毕就会自动-1,到0后,等待在countdown上的主线程才会继续往下进行。是个时间栅栏,是个时间点。
实际任务中有不少场景须要作些前提准备,那怎么准备呢,就是countdown了。
6.cyclicbarrier是个在时间线上切一条线。 与countdownlatch不一样的是能够循环的进行主线程工做。士兵集合完毕,下任务,任务完毕,继续执行,均可以用同个实例去进行。对于某个士兵被interrupted后,其余await的士兵以为没有可能继续下去的话会主动抛出brokenbarrierexception异常。目的是能够一组组的复用,功能强大些。这个用途在那里呢。
6. locksupport,可让线程挂起,和suspend不一样是,locksupport能够正常使用。它的思想有些信号量的许可,park需求许可,unpark若是发生在以前那是挂不住的。调用了不少native Api
7. reentrantLock,主要是应用的实现,没有用到不少底层的api。A.CAS状态(判断是否修改为果)。B.维护一个等待队列。C.park()在队列中等待,等unlock后再执行unpark()。
8. hashmap不是线程安全。List,set都提供了synchornize方法。这个做为实用的工具类,并发量小的状况。由于内部将全部的操做方法都加了synchronized,这就致使把全部的操做变成了串行的实现。
9. ConcurrentHashMap是个高并发。首先hashmap是个数组,每一个槽位都放一个链表头部,若是出现大量hash冲突,它就会退化成一个链表,因此hashmap通常不能放太满,太满了会有hash冲突,会致使性能下降。ConccurrentHashMap会在内部被拆分红不少segment,小的hashmap,每一个线程进入的entry可能不一样,减小了hash冲突。相应的put方法也没有同步,只是进行了cas运算。若是找有hash冲突就加入,那就把他串成一个链表,若是缺失是插入,不然是覆盖。若是这个尝试次数超过了最大次数,我就会暂时把本身挂起。若果有重hash的状况,可能尝试会重复作些事情。不会直接lock,是尽可能尝试后再lock。只不过有个小弊端,hashmap分段后,统计总数时须要得到全部的锁,毕竟不能在变更时候统计。只不过Size()可能也不是一个高频方法。
10. BlockingQueue,阻塞队列,不是个高性能的并发容器,是个多线程的共享容器。读的时候,若是为null,就会等待其余线程放数据,等待线程会唤醒并获取数据。若是写数据时为满,那么写数据就会等待别的线程拿掉数据才能写进去。只因此不是高性能的是由于加过了锁。Take能够await(),put能够signal。就是个生产消费模式。
11. ConcurrentlinkedQueue是个高性能的并发安全的队列,若是想在多线程有个性能好的表现就是这个了,不多把线程挂起的操做。
五:并发2
1. 线程池,线程的建立和销毁和业务无关,只关注线程执行业务。那么咱们放必定数量的常驻线程,就可能节省一些cpu时间。核心就是咱们要保留咱们的线程,工做作完了要作些特殊的事情避免线程的退出。若是有空闲线程,直接拿过来用,没有就会建立线程再执行。用完放回池里并等待,取出设置线程后会通知。
2. 一个稳定商用线程池。这个callable运行后有个返回值。
3. newfixedThreadPool(固定),newSingleThreadExecutor,(来一个执行一个),newCachedThreadPool(一段时间没有任务会慢慢减小),newScheduleThreadPool(好比每5分钟执行一个任务等,相似计划任务的功能),前3个实现本质是相同。都是指定了执行线程的数量和存活时间,而后后进的任务若是继续进入会不停的加入到队列中。newCachedThreadPool并不会开线程执行,而是有线程正好要拿和塞数据,起到了数据传输做用,而是执行。
4. ExecutorService能够获得线程执行先后的信息。
5. 若是有大片任务上来,发现负载过大,那么咱们就须要丢弃一些任务,有些拒绝的策略,有不一样的操做方式。RejectedExecution,若是出现异常会把异常信息打印出来。Discardpolicy,至二级丢弃。Callerrunspolicy,我作不了交给调用者来作。DiscardOldestpolicy,丢弃最老的没有处理的请求。这个案例说明在高负载的时候咱们该怎么作要好一些。
6. 咱们能够自定义一个线程工厂,好比名字,优先级,是否守卫进程等。
7. 简单线程池实现原理,提交一个任务去线程池中执行,若是没有可能放入一个workQueue的BlockingQueue中,ctl是个状态参数,代表有效的线程数,代表库是否runnig或shutting(running,shutdown,stop)等。若是咱们自定义的话,会把两个参数放在一个对象当中,就要有方法从中抽取出来。若是状态正确就会运行,不然会拒绝。
8. Forkjoin,就是拆分任务收集结果,分隔任务也是有边界的,若是分割完毕后,执行task.fork()推向线程池。Forkjoin主要是思想,小任务直接执行,大任务则分解。有一些个ForkJoinPool。Ctl中不少变量被打包在一个64位字节中,那比分拆的多字段有什么优点呢?这个就是避免多线程synchornize,影响性能的可能性,而这样作呢,一个cas操做就能完成。而多个变量也避免了多个cas的操做。这显然不用加锁的好处。
六:设计模式
System.out.println(Singleton.STATUS);时就会生成这个单例对象,可是可能你只是想访问变量却生成了这个单例。
5.这种方式相对好些,缘由是首次访问StaticSingleton时并不会初始化SingletonHolder,只有真正调用时候才进行初始化,后期调用直接返回便可。并且注意类的构造函数初始化用的是private。
7.确保父类是不可继承的,字段是不能从新修改的,保证类是不可变的。
9. 不变模式案例,String建立后不会改变,哪些看起来像修改了String内容的操做实际上也是在新String进行的操做。不少基础的数据类型其实都是不变模式的思想,包括Integer的i++实际都是封装在了自动拆箱装箱的过程当中。若是它在原程序上的修修补补就不能保证多线程的安全性了。
10. future模式,说白了就是异步操做。一个很好的比喻就是先当即返回给你个订单,至于数据之后再返回。先给你个人承若,其余事情你能够继续作。
11.若是FutureDate没有找到返回数据就会等待,等确实执行完设置了真实数据后进行线程的通知。
12.这个异步线程执行完毕会向原线程设置真是的数据。开启线程就会进行异步执行。这就是后台把同步的方式写成异步的调用方式。
12.这就是jdk封装图的future模式的支持,callable相对runnable能够返回相关的执行类型。只是想调用完了拿到一点返回值,能够把同步调用变成异步调用仍是个不错的用法。核心的原理仍是future的异步方式。
12. 生产消费模式,两个线程如何共享数据,双方怎么才能松散的知道呢,紧密的知道对方,一个模块对外知道的越少越好,甚至不知道更好。内存缓冲区。你们只知道缓冲区。Currentlinkedblockingqueue是个高并发的解决方案。
13. 消费者若是有很是多的数据要消费,要扣考虑性能的话,那么能够考虑把循环写死疯狂的得到数据。
七:NIO与并发的关系
1. 它改变了线程在应用层使用的一种方式,解决了一些实际的困难,节省了一些成本。
2. NIO的存储是基于block的,他以块为基本单位,为原始的类型都有buffer,原始的I/O是流,它支持Channel。锁存在的话,表示当前线程可能占用了这把锁,其余线程若是用这把锁就会等待,使用完了会删除掉锁,用文件作为锁,和原来的对比是用某个整数来的作为锁的。文件映射到内存比传统方式快的多。
3.NIO全部的读写都先经过channel读写到buffer中。
3. Buffer有3个重要的操做。
a. Position 写(当前的下个)(存后的位置,flip而后置0)
b. Capacity 缓存区总容量上限()
c. Limit 实际的容量小于等于容量(flip后limit到position位置)。
4. Flip一般以前的是写,以后的是读缓冲区。读写转换的时候一般使用。
5. 文件映射到内存,将整个文件读到内存中了,这个速度是比传统的快的。
6. NIO的网络编程,多线程网络服务器的通常结构。客户端被派发给线程作处理,固然也把socket给线程,每一个线程作处理。固然涉及到大量的r/w。
7. 这个的关键是从线程池中挑一个线程处理客户端请求。新线程作客户端的请求,主线程继续作8000端口监听的线程等待工做。Serversocket就像个tomcat的容器(先dispatcher线程给特定的action),而后action建立HandleMsg线程,进行业务的处理,并且都是针对客户端的读取和写出。
8. 这么作有什么问题呢?
若是某个客户端出现异常延时的话,网络多是不太稳定的,状况也比较多(通信信道并不可靠,中断延时确定会产生一些影响,数据的准备读取都是在线程中负责)若是并发大,就会对付这些卡死的线程。那么NIO读取是不阻塞的,只有准备好数据了线程才会真正开始工做,没准备好的话是不会启动工做的。若是是原始的读取操做每一个客户端都会卡6s钟
若是是NIO来处理,chanel相似socket的流,能够和文件或网络socket对应,一个线程对应一个selector,一个selector能够选择多个channel每一个channel对应一个socket,他要看那个客户端准备好了,就是看那个准备好了。
Select:会告诉当前有客户端准备好数据了,不然select调用select是会阻塞,若是有则返回selectionkey,这是一对select和chanel准备好的对象,这样这个线程就能够用少许线程监控大量客户端,那么总有几个客户端多是准备好的。那么直接拿过来用就行了。
Selectnow:和select功能相同,直接返回,若是有准备好的直接返回数据,没有的话就返回0,大部分时候select确实会进入等待。
9. 代码解释
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);//非阻塞不会作accept等待,而是说有人连上来,accept以后,我会获得一个accept通知。若是是阻塞模式那和传统的编程是相似的。
10. NIO是将数据准备好了再交由应用进行处理。把这个事交给专门少许线程来管理,为真正执行业务的线程节省了资源。只是把等待的时间在专门的线程中等待。
11. AIO,比NIO更进一步,说等读完了,写完了再来通知我。AIO速度并无加快,一个更合理线程和时间的调度,颇有ajax异步的感受。主要是定义大量的业务函数进行回调处理。
Server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(PORT));
Public abstract<A> void accept(A attachment,CompletionHandler<AsynchronousSocketChannel,? Super A> handler);
当真有accept信息上来后,才会真正调用。
12. AIO的异步方法是马上返回的。有的网络协议报文头部是固定的,因此用这种方法来过滤掉头部内容。Read(ByteBuffer[],int,int,long,TimeUnit,A,CompletionHandler<Long,? Super A>)。Write也有获得future,最后看写了多少内容。好比下面这个读写都是返回的future对象。这样作又有点像把异步的操做变成了同步的阻塞操做。
八.Java并发课程
1.锁有阻塞非阻塞的,非阻塞(无障,无锁),一旦用到了锁,那并发性能永远比不上无锁的方式。那怎么在阻塞状况下让性能获得优化,那么怎么让这种阻塞的影响降到最低。
2.若是是用try lock 这种cas不认为是锁,不挂起。
a.下降持有锁的时间,下降同时进入临界区的时间。
Public synchronized void syncMethod(){ meth1();meth2();meth3();}减小
Public void syncMethod(){ meth1();synchronized(this){ meth2()};meth3();}减小持有时间和范围缩小,减小冲突性,这个synchronized(this),加锁的this确实是那个对象的锁。
B.减少锁粒度,一个对象可能不少线程访问,拆分对象,增长并行度,下降锁竞争,偏向锁,轻量锁成功率提升。好比hashmap的同步实现读写会互相阻塞的。
那么concurrentHashMap就是把hashmap拆分红多个segment后,粒度减少后,concurrenthashmap就容许若干个线程同时进入。
C.锁分离,就是读写锁的思想,读读不影响(基本是无等待并发),写写同步,读多写少的状况下能提升性能。若是说读写分离的延生的话,就是让尽可能减小冲突区域和冲突时间。LinkedBlockingQueue一个从头部,一个从尾拿,用热点分离的方法。
D.锁粗化,当重复的得到释放频率太高,致使锁同步的判断性能影响大于了加锁的性能时还不如加大锁的宽度。像这种状况的1个锁性能优于多个锁。
好比,for循环的加锁状况优化。除非说for内的内容太多也等不起,就在内部加锁让线程竞争执行更好。
因此锁是个度的平衡。
E.锁消除,StringBuffer居然是同步的。看局部方法是否多是全局影响的变量,有的能够开启逃逸分析。JDK判断是否须要移除锁的限制。
F.虚拟机内的锁优化
九:锁偏向,每一个对象有对象头,MarkWord,描述hash,锁,垃圾标记,年龄,保存一些对象信息。
十:偏向锁,很偏爱,偏向当前已占有锁的线程。有时会出现某线程不停的请求同一把锁,这是有可能的,若是你已经占有这个锁了,那么你就进去。锁是一种悲观的策略,竞争,冲突是可能有的,实际不少状况竞争多是不存在的,或着并不激烈,系统已经发现我是被偏向的那个线程,那么我就是否是偏向模式,而且是我,那我就直接不用锁的操做进到锁里,提升的进入速度和性能。若是没有竞争并反复请求同个锁,那么效率的改善是很明显的。实现的时候,对象标记mark为偏向,而且将线程id写入对象Mark就行了。但竞争激烈的状况下偏向锁反而是种负担。全部任何事情都是有两面性的。
十一:轻量级锁,最好不用动用操做系统级的互斥,而最好在JVM应用层面解决这个问题。判断某线程是否持有某对象锁,那么只要看看lock是否设置了某对象的mark值,若是是的话,那么就说锁持有对象,对象头的指针指向了lock,lock呢又存在thread的栈空间当中,判断线程是否持有对象的锁,只要判断对象头部指向的位置是否在线程栈的地址空间当中。
若是轻量锁失败,说明有竞争,升级为常规锁(操做系统层面的同步)。若是竞争激烈,轻量锁会作不少额外操做,致使性能降低。说白了就是尝试拿锁的一个cas操做。
十二:自旋锁
若是轻量级锁失败的话,可能会动用自旋锁,那可能会试着while try lock的操做,而不要直接挂起,由于消耗时钟周期太长了。(像现实中同样,等待的代价和时钟周期远远大于不断增长的尝试)。有时候可能别人会把资源释放掉了,对于快速的cpu来讲等待的代价大于不停的尝试。说的是减小减小锁的持有时间,那么自旋的成功几率就会上升。更可能避免线程的挂起。轻量锁和自旋锁都是在JVM层面作的一些优化。
十三:这个代码很是隐蔽呀,Integer的i不是一个
十四:ThreadLocal,不一样的线程只访问本身的对象实例,那么锁就不必存在。
若是某对象被多线程访问时可能会致使对象异常,固然能够加锁,可是高并发性能会有限制,那么咱们就能够用ThreadLocal的思想来定义本身使用的变量。咱们知道SimpleDateFormat是线程不安全的。在hibernate的connection的保存中用了ThreadLocal类,一些公共类,工具类,每一个本身的线程持有一份,不像vector数据间会相互影响。(这个实现的原理还没太懂,须要了解下hashmap的原理),这是ThreadLocal的基本结构图。
补充hashmap原理:
a.就是利用数组的查找性能和链表的添加修改的性能优势。
Hash(k)%len = slot_index,hash(k)是一个非一一映射的int值函数,计算出的值是肯定的 。因此可能hash冲突。实际存在的是一个Entry[] table。
十五:并发调试
1. 线程dump及分析。若是线程卡死了。多线程执行是不肯定顺序的而且不能保证问题的重现,那么怎么手动控制多线程的调试呢?设置条件断点是中方法。先知道这块有个条件断点的事,不知足条件的就进不来了。Suspend VM能够中断整个虚拟机,会断全部线程,有时可能会死掉。
2. 线程dump分析jstack.有时能够分析出死锁或死循环的线程。断点文件是能够调试的,前提是先链接上资源。
3. 好比下面的异常信息,能够用条件断点的多线程临界点来调试。
public boolean add(Object obj)
{
ensureCapacity(size + 1);
1.t1,t2都判断经过而且认为知足容量限制
elementData[size++] = obj;
2. t2设置值完毕后,t1并不知道size已是11了,而此时扩容的条件判断已经作过了,容量并无获得扩充,index=11这个单元并无分配空间,因此t1此时设置值时候就会抛出异常(
Exception in thread "t1" java.lang.ArrayIndexOutOfBoundsException: 7747
at java.util.ArrayList.add(ArrayList.java:352)
at Test$RT.run(Test.java:19)
at java.lang.Thread.run(Thread.java:662)
)
return true;
}
3. 使用命令,jstack –l 进程号,有时候能够查看一些死锁信息。有的也并不必定是死锁,只是占用某些资源不释放。
十五:JDK8并发新支持
1. LongAdder,也是相似于热点分离成,拆分红多个增阿基cas操做的成功性。
好比 long add会把整数成cell的数组,多线程进入操做打散的cell时减小冲突的可能性。淡然有的并发小的时候,初始1个cell,当执行有冲突的时候会扩大cell的数量,而且并发增多的时候可能还会往上扩充,而后分配映射,目的仍是减小冲突。
这个有些有冲突的优化算法,
2.CompletableFuture是future模式的加强版,特色是它会把完成的点开放出来供你们用。
这个接口更有一些函数式编程的倾向在里面。
2. stampedLock,有读写锁的有点,rr不阻塞。并且有改进的地方时是在读的时候也不阻塞写,只是读的时候判断有写时会进行重读。当有大量读操做,而写操做不多的时候,可能写操做会有饥饿现象。同时的理解是写时候能够很容易的拿到读锁,当读后发现数据不一致会重新读。
3. 先采用乐观读,若是读取成功就执行修改,读取没有成功就退化成悲观读。
4. stampedLock采用了CLH自旋锁的思想。锁维护一个等待队列,对象放到队列里,对象有一个锁的标记位。每一个加入的对象会不断的循环等待前面对象释放锁。由于当前对象在循环中,因此并不会挂起,可是有必定的循环次数去等待,并不会不休止的循环,当达到必定次数后就会将线程等待。
Jetty:
1.jetty是个http server,甚至能够作一个嵌入式的引入,new了并启动起来。看他后面是如何用多线程实现高吞吐量和性能。
New Server()(须要实例化,把待执行的放入线程池中)
Server start()(须要启动,ScheduleExecutorSecheduler (定时调度),ByteBufferPool(是个可复用对象池,好比NIO中的channel不用每次去new,里面有特别的安全的无锁线程池-ArrayByteBufferPool),)
http请求(servlet,容器,jsp等)
2. 服务器原理是相通的,若是一个线程发起,那么就放到线程池的队列里就行了,是个BlockingQueue,那么多是个优化的点。对象池的对象是类似的,ByteBufferPool内的对象大小不一样,不可能把全部大小的内容都new出来,Bucket有size和queue,queue是延时加载的。Acquire得到,找到合适的bucket。Release,找到合适大小的bucket,归还buffer,清空buffer的标志位,归还到queue中去。若是ByteBuffer过大太小没法归还,会被GC回收。它是无锁的,大小不一样处理不一样。ConnectionFactory,accept后,要建立一个对象来维护链接。会得到cpu的数量,由cpu数量算出线程数量,那么会分配几个线程等待链接。建立acceptor线程组。初始化serverconnectorManager,而后会计算出有多少个selector来作这个等待。总之selectoer要比accept多。 设置端口,关联server.
doStart 有个lifecycle的管理器方法,相似spring 对bean的管理。拿到线程池,每一个connector有多个acceptor,acceptor有会有多个线程,若是须要的线程多余200,那么启动ThreadPool启动并从队列中拿出任务去执行,固然还有AppContext的一些功能参数去维护。启动connector,得到connectionFactory,根据seelctor的数量启动selector,而后等待acceptor进行链接,建立线程并执行,SocketChannel channel = ServerConnector.accept(),若是accept为0,那么会配置成阻塞模式。http请求,accept成功后配置为非阻塞,配置socket,选择可用的managedSelector线程,虽然不是线程安全的也能够。提交任务到执行队列中,ConcurrentArrayQueue保存元素而不是node,须要更少GC,更少对象(真是越到底部优化的越精细呀),因此性能更好一些,因此它是个高性能高并发的实现,因此即使有大量任务上来。ManagedSelector.run()方法runChanges()。而后得到SelectionKey=channel.selector,得到链接对象。而后等待select()操做,确实会用新的线程进行业务的处理,不会占用不少selected()线程。中间仍是有不少关联的。