张振华.JackQQ:494460705Mail:zhangzhenhua846@126.com2015年6月10年Java开发1自我介绍Java多线程从入门到精通张振华.Jack------Java并发编程详解目录I.JAVA-Thread概念II.Thread的三种实现方法III.生命周期IV.安全和锁V.Concurrent包(安全集合类、安全Queue)VI.线程阻塞机制VII.线程池详解(原理,实际使用)VIII.线程的监控,分析方法IX.扩展数据库连接池张振华.Jack2概念A.什么是进程A.是资源分配的最小单位;(资源,包括各种表格、内存空间、磁盘空间)B.同一进程中的多条线程将共享该进程中的全部系统资源B.什么是线程A.线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成。寄存器可被用来存储线程内的局部变量B.线程是CPU调度的最小单位C.什么是并行和并发A.并行运行:总线程数=CPU数量*核心数:B.并发运行:总线程数CPU数量*核心数:(时间片轮转进程调度算法)3张振华.JackThread的三种实现方法4张振华.Jack线程的生命周期/常用方法获得当前线程的副本:Threadcurrent=Thread.currentThread();例如多线程上传的文件的进度条可以使用5张振华.Jack什么是安全的?先问个问题是不是不加锁就不是线程安全的,个人感觉只要你代码里面没有变量互串,线程之间互不影响,例如server的设计方法;非单例代码也是安全的,注意只有单例模式下才会有锁的问题何为安全6张振华.Jack锁一.隐式锁:synchronized(同一个对象锁下面的,synchronized区域是互斥的)二.方法锁(默认是当前对象的锁)三.代码快锁(性能高于方法锁,可以指定哪个对象的锁)四.显示锁:java.util.concurrent.lock(需要手动关/开),注意自己的代码逻辑不要产生死锁了五.关键字:volatile(线程在每次使用变量的时候,都会读取变量修改后的最的值)六.其实是有风险的,并行情况下不一定正确,有可能两个线程同时取到最后修改的值七.原子操作:java.util.concurrent.atomic(AtomicBoolean,AtomicLong,AtomicInteger等一些类库)八.(如果查看源码的话,实现原理是volatile,相对安全的,个人觉得不绝对)7张振华.Jack问一个问题,请看以下下面的锁有用吗8张振华.Jack这种锁是没有用的啊,因为synchronized的对象不一样9张振华.Jack常用的Concurrent线程安全类库java.util.concurrent.ConcurrentHashMapjava.util.concurrent.ConcurrentLinkedQueuejava.util.concurrent.ConcurrentMapjava.util.concurrent.ConcurrentNavigableMapjava.util.concurrent.ConcurrentSkipListMapjava.util.concurrent.ConcurrentSkipListSet实现原理和算法类似lock的10张振华.Jack安全线程阻塞队列(1)java.util.concurrent.BlockingQueue11张振BlockingQueue很好的解决了多线程中高效安全“传输”数据的问题;基于java.util.Queue的基础上做了一些线程安全的封装;(对以下方法做了一些的扩展和封装)1.ArrayBlockingQueue基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,这是一个常用的阻塞队列,除了一个定长数组外,ArrayBlockingQueue内部还保存着两个整形变量,分别标识着队列的头部和尾部在数组中的位置;2.LinkedBlockingQueue基于链表的阻塞队列,同ArrayListBlockingQueue类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成)3.DelayQueueDelayQueue中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素。DelayQueue是一个没有大小限制的队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会被阻塞。使用场景:DelayQueue使用场景较少,但都相当巧妙,常见的例子比如使用一个DelayQueue来管理一个超时未响应的连接队列。4.PriorityBlockingQueue基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定),但需要注意的是PriorityBlockingQueue并不会阻塞数据生产者,而只会在没有可消费的数据时,阻塞数据的消费者。5.SynchronousQueue1)种无缓冲的等待队列,同步队列没有任何内部容量,甚至连一个队列的容量都没有;2)其中每个put必须等待一个take,反之亦然。。3)无锁的机制实现;(可想而知高并发的时候性能肯定是最高的)张振华.Jack12安全线程阻塞队列(2)BlockingQueue常用的儿子线程伐(1)java.util.concurrent.CountDownLatchCountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0时,await()方法会阻塞程序继续执行;【实际场景,如:开5多线程去下载,当5个线程都执行完了才算下载成功!】13张振华.Jack线程伐(2)java.util.concurrent.CyclicBarrier【应用场景】我们需要统计全国的业务数据。其中各省的数据库是独立的,也就是说按省分库。并且统计的数据量很大,统计过程也比较慢。为了提高性能,快速计算。我们采取并发的方式,多个线程同时计算各省数据,每个省下面又用多线程,最后再汇总统计CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点(commonbarrierpoint)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时CyclicBarrier很有用。因为该barrier在释放等待线程后可以重用,所以称它为循环的barrier。14张振华.Jack15张振华.Jack16张振华.Jack线程伐(3)(信号装置)java.util.concurrent.Semaphore概念:就像一个排队进入上海博物馆一样,放几个人等一下,有几个人走了然后再放几个人进入;像是一种排队机制;定义:一个计数信号量。从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。张振华.Jack1718张振华.Jack线程伐(4)(任务机制)java.util.concurrent.Future-FutureTask1:一般FutureTask多用与耗时的计算,主线程再完成自己的任务后,再去获取结果。2:只有在计算完成时获取,否则会一直阻塞直到任务完成状态。前面有提到例子,这里不多说了线程池概念先看三个例子后解释;19张振华.Jack实际例子:tomcat的线程池和nginx的线程池线程池常用的方法newFixedThreadPool(固定大小线程池)张振华.Jack20注意查看heap占用和执行时间和线程数张振华.Jack211:内存大小不变;2:线程数量不变;3:不会流量上来的时候一下子给服务器弄死机;4:相对于newCachedThreadPool来说,线程池所占的资源永远都不会释放;而newCachedThreadPool里面的线程,不是使用的时候资源会自动回收!线程池常用的方法newCachedThreadPool(无界线程池,可以进行自动线程回收)张振华.Jack22张振华.Jack231:内存顿时上升;2:瞬间创建了50个线程,每5个一并发在等待执行;3:如果内存允许,CPU允许,执行的肯定比上面固定的快;线程池常用的方法newSingleThreadExecutor(单个后台线程)张振华.Jack24顾名思义:只能有一个线程在执行,就不多说了,内存使用更少,但同时执行的时间也会更长;线程池的概念I.单个任务处理的时间比较短(如:互联网)II.将需处理的任务的数量大III.接受突发性的大量请求,不至于当机;IV.使用线程池的好处:1.减少在创建和销毁线程上所花的时间以及系统资源的开销2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。张振25线程池原理张振26知道个大概,具体也没仔细研究,没打算自己写个线程池的底层!认识线程池ExecutorService;接口类;ThreadPoolExecutor的接口;线程池的可执行服务;ThreadPoolExecutor通过这个类可以自定义线程池张振27publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueueRunnableworkQueue,ThreadFactorythreadFactory,RejectedExecutionHandlerhandler)线程池认识主要参数(一)corePoolSize-池中所保存的线程数,包括空闲线程。maximumPoolSize-池中允许的最大线程数。keepAliveTime-当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。unit-keepAliveTime参数的时间单位。workQueue-执行前用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务。threadFactory-执行程序创建新线程时使用的工厂。handler-(异常处理程序)由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。张振28queue上的三种类型直接提交。SynchronousQueue。无界队列。使用无界队列(例如,不具有预定义容量的LinkedBlockingQueue)有界队列。当使用有限maximumPoolSizes时,有界队列(如ArrayBlockingQueue)有助于防止资源耗尽。张振29RejectedExecutionHandler来处理线程池处理失败的任务;AbortPolicy(默认):这种策略直接抛出异常,丢弃任务。DiscardPolicy:不能执行的任务将被删除;这种策略和AbortPolicy几乎一样,也是丢弃任务,只不过他不抛出异常。DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务被删除,然后重试执行程序(如果再次失败,则重复此过程)当然也可以自定义.ThreadFactory:默认情况下为Executors.defaultThreadFactory():我们可以采用自定义的ThreadFactory工厂,增加对线程创建与销毁等更多的控制,线程池认识主要参数(二)问个问题?一.Web项目中咱们常用的线程池是不是单利的?二.我见过有的同事,在services方法里面去创建线程池,这是不可以去的,因为每当这个方法被调用的时候不是创建多少个线程的问题了,而是创建出来了一大堆线程池!张振30线程的监控方法(1)全手动top-p12377–H查看java进程的有哪些线程的运行情况;Jstack生成线程栈dump的信息然后将上面的pid转换成16进制到dump文件