Java基础 进程与线程3

线程池  补

Executors创建线程池

         newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,
可灵活回收空闲线程,若无可回收重用时则新建线程

new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,
       new SynchronousQueue<Runnable>());
用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步
任务

Integer.MAX_VALUE--OOM

        newFixedThreadPool 创建一个固定大小的定长线程池,可控制线程最大并发数,
超出的线程会在队列中等待

        new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());

        因为采用无界的阻塞队列,所以实际线程数量永远不会变化,适用于可以预测线程数量
的业务中,或者服务器负载较重,对当前线程数量进行限制

newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行
可以延时启动,定时启动的线程池,适用于需要多个后台线程执行周期任务的场景
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService

 super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());

super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());

        newWorkStealingPool:创建一个拥有多个任务队列的线程池,可以减少连接数
创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行  

ExecutorService提供提交任务的方式
  线程池框架提供了两种方式提交任务,submit()和execute(),
  通过submit()方法提交的任务可以返回任务执行的结果,
  通过execute()方法提交的任务不能获取任务执行的结果。 

 ExecutorService的方法:关闭线程池
 shutdownNow:对正在执行的任务全部发出interrupt(),停止执行,对还未开始执
 行的任务全部取消,并且返回还没开始的任务列表
 
 shutdown:当调用shutdown后,线程池将不再接受新的任务,但也不会去强制终止
 已经提交或者正在执行中的任务

Java内存模型

Java内存模型定义了一种多线程访问Java内存的规范。

Java内存模型将内存分为了主内存和工作内存。类的状态也就是类之间共享的变量,是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在自己的工作内存中有一份拷贝,运行自己线程代码的时候,用到这些变量,操作的都是自己工作内存中的那一份。在线程代码执行完毕之后,会将最新的值更新到主内存中去
定义了几个原子操作,用于操作主内存和工作内存中的变量
定义了volatile变量的使用规则
happens-before即先行发生原则,定义了操作A必然先行发生于操作B的一些规则,比如在同一个线程内控制流前面的代码一定先行发生于控制流后面的代码、一个释放锁unlock的动作一定先行发生于后面对于同一个锁进行锁定lock的动作等等,只要符合这些规则,则不需要额外做同步措施,如果某段代码不符合所有的happens-before规则,则这段代码一定是线程非安全的

synchronized

对于synchronized语句当Java源代码被javac编译成bytecode的时候,会在同步代码块的
入口位置和退出位置分别插入monitorenter和monitorexit(2个)字节码指令。而
synchronized方法则会被翻译成普通的方法调用和返回指令,在VM字节码层面并没有任何
特别的指令来实现被synchronized修饰的方法,而是在Class文件的方法表中将该方法的
access_flags字段中的synchronized标志位置1,表示该方法是同步方法并使用调用该方
法的对象或该方法所属的Class在JVM的内部对象表示Class做为锁对象 

执行monitorenter指令时,线程会为锁对象关联一个ObjectMonitor对象(c++)。
线程遇到synchronized同步时,先会进入ObjectMonitor对象的EntryList队列中
然后尝试把ObjectMonitor对象的owner变量设置为当前线程,同时ObjectMonitor
对象的monitor中的计数器count加1,即获得对象锁。否则通过尝试自旋一定次数加锁
,失败则进入ObjectMonitor对象的cxq队列阻塞等待

同步语句块使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令指向同步代码块的结束位置
方法的同步是隐式的,也就是说 synchronized 修饰方法的底层无需使用字节码来控制。

synchronized修饰的方法并没有使用 monitorenter 和 monitorexit 指令,取得代之是ACC_SYNCHRONIZED 标识,该标识指明了此方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。这就是 synchronized 锁在同步代码块上和同步方法上的实现差别。

Monitor 对象
 任何对象都关联了一个管程,管程就是控制对象并发访问的一种机制。
 可以理解 synchronized 就是 Java 中对管程的实现。管程提供了一
 种排他访问机制,这种机制也就是互斥。互斥保证了在每个时间点上,
 最多只有一个线程会执行同步方法。所以理解了 Monitor 对象其实就是
 使用管程控制同步访问的一种对象。


 monitor对象是monitor机制的核心,它本质上是jvm用c语言定义的一个
 数据类型。对应的数据结构保存了线程同步所需的信息,比如保存了被阻
 塞的线程的列表,还维护了一个基于mutex的锁,monitor的线程互斥就是
 通过mutex互斥锁实现的。

synchronized底层语义原理
 * 
 * Synchronized是通过对象内部的一个叫做监视器锁monitor来实现的,
 监视器锁本质又是依赖于底层的操作系统的Mutex Lock互斥锁来实现的
 。而操作系统实现线程之间的切换需要从用户态转换到核心态,这个成
 本非常高,状态之间的转换需要相对比较长的时间,这就是为什么
  Synchronized效率低的原因。因此,这种依赖于操作系统Mutex Lock
 所实现的锁称之为重量级锁。
 
 Java SE 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”
 和“轻量级锁”:锁一共有4种状态,级别从低到高依次是:无锁状态、偏向
 锁状态、轻量级锁状态和重量级锁状态。锁可以升级但不能降级。
  
 偏向锁是JDK1.6中引用的优化,它的目的是消除数据在无竞争情况下的同步
 原语,进一步提高程序的性能。
 
 轻量级锁也是在JDK1.6中引入的新型锁机制。它不是用来替换重量级锁的,它
 的本意是在没有多线程竞争的情况下,减少传统的重量级锁使用操作系统互斥
 量产生的性能消耗
 
 Synchronized的重量级锁是通过对象内部的一个叫做监视器锁monitor来实现
 的,监视器锁本质又是依赖于底层的操作系统的Mutex Lock互斥锁来实现的。
 而操作系统实现线程之间的切换需要从用户态转换到核心态,这个成本非常高,
 状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值