线程:由浅入深
线程
操作系统在分配资源时候是把资源分配给进程的,但是 CPU 资源就比较特殊,它是分派到线程的,因为真正要占用 CPU 运行的是线程,所以也说线程是 CPU 分配的基本单位。
如图一个进程中有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器,栈区域。
其中程序计数器用来记录线程当前要执行的指令地址, CPU 一般是使用时间片轮转方式让线程轮询占用的,当线程 CPU 时间片用完后,要让出 CPU,等下次轮到自己时候再执行,那么如何知道之前程序执行到哪里了?其实程序计数器就是为了记录该线程让出 CPU 时候的执行地址,待再次分配到时间片时候就可以从自己私有的计数器指定地址继续执行了。
栈资源,用于存储该线程的局部变量,这些局部变量是该线程私有的,其它线程是访问不了的。
堆是进程中最大的一块内存,被进程中的所有线程共享的,是进程创建时候分配的,堆里面主要存放使用 new 操作创建的对象实例。方法区则是用来存放进程中的代码片段的。
对于多线程程序,从微观上来讲某一时刻只有一个线程在工作,多线程目的是让 CPU 忙起来。即采用多线程不会提高程序的执行速度,反而会降低速度,但是对于用户来说,可以减少用户的响应时间。
synchronized 关键字
底层实现:进入时,执行 monitorenter,将计数器 +1,释放锁 monitorexit 时,计数器-1;当一个线程判断到计数器为 0 时,则当前锁空闲,可以占用;反之,当前线程进入等待状态。
ThreadLocal(线程局部变量)关键字
当使用 ThreadLocal 维护变量时,其为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不会影响其他线程对应的副本。
ThreadLocal 内部实现机制:
线程池
多线程开发一般使用线程池的方式来创建和管理线程,原因不用多说。Java提供了一些简单易用的线程池,其实是对原生线程池做了简化包装。
*corePoolSize 核心池的大小
默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到 corePoolSize 后,就会把到达的任务放到缓存队列当中。
*maximumPoolSize 线程池最大线程数
它表示在线程池中最多能创建多少个线程
*keepAliveTime
默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize。线程空闲的时间达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。
*unit 参数 keepAliveTime 的时间单位
*workQueue
一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般有以下这几种选择:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue。
*threadFactory 线程工厂,主要用来创建线程
*handler 拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常;
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常;
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务;
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
未完 ... ...