内存模型
为了保证共享内存的正确性(可见性、有序性、原子性),内存模型定义了共享内存系统中多线程程序读写操做行为的规范。经过这些规则来规范对内存的读写操做,从而保证指令执行的正确性。它与处理器有关、与缓存有关、与并发有关、与编译器也有关。他解决了CPU多级缓存、处理器优化、指令重排等致使的内存访问问题,保证了并发场景下的一致性、原子性和有序性。java
内存模型解决并发问题主要采用两种方式:限制处理器优化和使用内存屏障。编程
ava程序是须要运行在Java虚拟机上面的,Java内存模型(Java Memory Model ,JMM)就是一种符合内存模型规范的,屏蔽了各类硬件和操做系统的访问差别的,保证了Java程序在各类平台下对内存的访问都能保证效果一致的机制及规范。数组
Java内存模型规定了全部的变量都存储在主内存中,每条线程还有本身的工做内存,线程的工做内存中保存了该线程中使用到的变量的主内存副本拷贝,线程对变量的全部操做都必须在工做内存中进行,而不能直接读写主内存。不一样的线程之间也没法直接访问对方工做内存中的变量,线程间变量的传递均须要本身的工做内存和主存之间进行数据同步进行。缓存
而JMM就做用于工做内存和主存之间数据同步过程。他规定了如何作数据同步以及何时作数据同步。多线程
主内存主要对应于Java堆中的对象实例数据部分。工做内存则对应于虚拟机栈中的部分区域。并发
JMM是一种规范,目的是解决因为多线程经过共享内存进行通讯时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。目的是保证并发编程场景中的原子性、可见性和有序性。性能
在开发多线程的代码的时候,咱们能够直接使用synchronized
等关键字来控制并发,历来就不须要关心底层的编译器优化、缓存一致性等问题。因此,Java内存模型,除了定义了一套规范,还提供了一系列原语,封装了底层实现后,供开发者直接使用。优化
原子性
在Java中,为了保证原子性,提供了两个高级的字节码指令monitorenter
和monitorexit
。在synchronized的实现原理文章中,介绍过,这两个字节码,在Java中对应的关键字就是synchronized
。atom
所以,在Java中可使用synchronized
来保证方法和代码块内的操做是原子性的。spa
可见性
Java内存模型是经过在变量修改后将新值同步回主内存,在变量读取前从主内存刷新变量值的这种依赖主内存做为传递媒介的方式来实现的。
Java中的volatile
关键字提供了一个功能,那就是被其修饰的变量在被修改后能够当即同步到主内存,被其修饰的变量在每次使用以前都从主内存刷新。所以,可使用volatile
来保证多线程操做时变量的可见性。
除了volatile
,Java中的synchronized
和final
两个关键字也能够实现可见性,只不过实现方式不一样。
有序性
在Java中,可使用synchronized
和volatile
来保证多线程之间操做的有序性。实现方式有所区别:
volatile
关键字会禁止指令重排。synchronized
关键字保证同一时刻只容许一条线程操做。
好了,这里简单的介绍完了Java并发编程中解决原子性、可见性以及有序性可使用的关键字。读者可能发现了,好像synchronized
关键字是万能的,他能够同时知足以上三种特性,这其实也是不少人滥用synchronized
的缘由。
可是synchronized
是比较影响性能的,虽然编译器提供了不少锁优化技术,可是也不建议过分使用。
synchronized 关键字和 volatile 关键字的区别
synchronized关键字和volatile关键字比较
volatile关键字是线程同步的轻量级实现,因此volatile性能确定比synchronized关键字要好。可是volatile关
键字只能用于变量而synchronized关键字能够修饰方法以及代码块。synchronized关键字在JavaSE1.6以后进
行了主要包括为了减小得到锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各类优化以后执行
效率有了显著提高,实际开发中使用 synchronized 关键字的场景仍是更多一些。
多线程访问volatile关键字不会发生阻塞,而synchronized关键字可能会发生阻塞
volatile关键字能保证数据的可见性,但不能保证数据的原子性。synchronized关键字二者都能保证。
volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访
问资源的同步性。
线程池
线程池提供了一种限制和管理资源(包括执行一个任务)。 每一个线程池还维护一些基本统计信息,例如已完成任务
的数量。
使用线程池的好处:
下降资源消耗。 经过重复利用已建立的线程下降线程建立和销毁形成的消耗。
提升响应速度。 当任务到达时,任务能够不须要的等到线程建立就能当即执行。
提升线程的可管理性。 线程是稀缺资源,若是无限制的建立,不只会消耗系统资源,还会下降系统的稳定性,
使用线程池能够进行统一的分配,调优和监控。
实现Runnable接口和Callable接口的区别
若是想让线程池执行任务的话须要实现的Runnable接口或Callable接口。 Runnable接口或Callable接口实现类均可
以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。二者的区别在于 Runnable 接口不会返回结果但
是 Callable 接口能够返回结果。
执行execute()方法和submit()方法的区别是什么呢?
1) execute() 方法用于提交不须要返回值的任务,因此没法判断任务是否被线程池执行成功与否;
2)submit()方法用于提交须要返回值的任务。线程池会返回一个future类型的对象,经过这个future对象能够判断
任务是否执行成功,而且能够经过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用
get(long timeout,TimeUnit unit) 方法则会阻塞当前线程一段时间后当即返回,这时候有可能任务没有执行
完。
Atomic 原子类
Atomic 是指一个操做是不可中断的。即便是在多个线程一块儿执行的时候,一个操做一旦开始,就不
会被其余线程干扰。
并发包 java.util.concurrent 的原子类都存放在java.util.concurrent.atomic 下
JUC 包中的原子类是哪4类?基本类型使用原子的方式更新基本类型AtomicInteger:整形原子类AtomicLong:长整型原子类AtomicBoolean :布尔型原子类数组类型使用原子的方式更新数组里的某个元素AtomicIntegerArray:整形数组原子类AtomicLongArray:长整形数组原子类AtomicReferenceArray :引用类型数组原子类引用类型AtomicReference:引用类型原子类AtomicStampedRerence:原子更新引用类型里的字段原子类AtomicMarkableReference :原子更新带有标记位的引用类型对象的属性修改类型AtomicIntegerFieldUpdater:原子更新整形字段的更新器AtomicLongFieldUpdater:原子更新长整形字段的更新器AtomicStampedReference :原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,能够解决使用 CAS 进行原子更新时可能出现的 ABA 问题。