串行是指任务一个一个执行,同时只能执行一个任务,前一个任务没有执行完不能开始下一个
并行是指同一时间两个任务互不干扰,齐头并进,有两个核心在执行任务
并发就是只有一个核心,两个任务交替执行,一个执行一半再执行另一个,执行一半再换回来,在时间段上来看,两个任务好像是在一起执行的,其实是cpu在快速切换,但是在同一个时间点,只有一个任务在执行,另一个在等待
原子性
原子性是指在一个操作中cpu不可以在中途暂停然后再调度,即不被中断操作,要不全部执行完成,要 不都不执行。就好比转账,从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元, 往账户B加上1000元。2个操作必须全部完成。
可见性
当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定 还是之前的,线程1对变量的修改线程2没看到这就是可见性问题。
有序性
虚拟机在进行代码编译时,对于那些改变顺序之后不会对最终结果造成影响的代码,虚拟机不一定会按 照我们写的代码的顺序来执行,有可能将他们重排序。实际上,对于有些代码进行重排序之后,虽然对 变量的值没有造成影响,但有可能会出现线程安全问题。
volatile本身就包含了禁止指令重排序的语义,而synchronized关键字是由“一个变量在同一时刻只允许 一条线程对其进行lock操作”这条规则明确的。 synchronized关键字同时满足以上三种特性,但是volatile关键字不满足原子性。
在某些情况下,volatile的同步机制的性能确实要优于锁(使用synchronized关键字或 java.util.concurrent包里面的锁),因为volatile的总开销要比锁低。 我们判断使用volatile还是加锁的唯一依据就是volatile的语义能否满足使用的场景(原子性)
volatile
1.保证被volatile修饰的共享变量对所有线程总是可见的,也就是当一个线程修改了一个被volatile修 饰共享变量的值,新值总是可以被其他线程立即得知。
2.禁止指令重排序优化。
1、降低资源消耗;提高线程利用率,降低创建和销毁线程的消耗。
2、提高响应速度;任务来了,直接有线程可用可执行,而不是先创建线程,再执行。
3、提高线程的可管理性;线程是稀缺资源,使用线程池可以统一分配调优监控。
corePoolSize 代表核心线程数,也就是正常情况下创建工作的线程数,这些线程创建后并不会 消除,而是一种常驻线程
maxinumPoolSize 代表的是最大线程数,它与核心线程数相对应,表示最大允许被创建的线程 数,比如当前任务较多,将核心线程数都用完了,还无法满足需求时,此时就会创建新的线程,但 是线程池内线程总数不会超过最大线程数
keepAliveTime 、 unit 表示超出核心线程数之外的线程的空闲存活时间,也就是核心线程不会 消除,但是超出核心线程数的部分线程如果空闲一定的时间则会被消除,我们可以通过 setKeepAliveTime 来设置空闲时间
workQueue 用来存放待执行的任务,假设我们现在核心线程都已被使用,还有任务进来则全部放 入队列,直到整个队列被放满但任务还再持续进入则会开始创建新的线程
ThreadFactory 实际上是一个线程工厂,用来生产线程执行任务。我们可以选择使用默认的创建 工厂,产生的线程都在同一个组内,拥有相同的优先级,且都不是守护线程。当然我们也可以选择 自定义线程工厂,一般我们会根据业务来制定不同的线程工厂
Handler 任务拒绝策略,有两种情况,第一种是当我们调用 shutdown 等方法关闭线程池后,这 时候即使线程池内部还有没执行完的任务正在执行,但是由于线程池已经关闭,我们再继续想线程 池提交任务就会遭到拒绝。另一种情况就是当达到最大线程数,线程池已经没有能力继续处理新提 交的任务时,这是也就拒绝
1、一般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务 了,阻塞队列通过阻塞可以保留住当前想要继续入队的任务。
阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资 源。
阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂 起,从而维持核心线程的存活、不至于一直占用cpu资源
2、在创建新线程的时候,是要获取全局锁的,这个时候其它的就得阻塞,影响了整体效率。
就好比一个企业里面有10个(core)正式工的名额,最多招10个正式工,要是任务超过正式工人数 (task > core)的情况下,工厂领导(线程池)不是首先扩招工人,还是这10人,但是任务可以稍微积 压一下,即先放到队列去(代价低)。10个正式工慢慢干,迟早会干完的,要是任务还在继续增加,超 过正式工的加班忍耐极限了(队列满了),就的招外包帮忙了(注意是临时工)要是正式工加上外包还 是不能完成任务,那新来的任务就会被领导拒绝了(线程池的拒绝策略)。
线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过 Thread 创建线程时的 一个线程必须对应一个任务的限制。 在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对 Thread 进行了封装,并不是每次执行任务都会调用 Thread.start() 来创建新线程,而是让每个线程去 执行一个“循环任务”,在这个“循环任务”中不停检查是否有任务需要被执行,如果有则直接执行,也就 是调用任务中的 run 方法,将 run 方法当成一个普通的方法执行,通过这种方式只使用固定的线程就 将所有任务的 run 方法串联起来。
spring是什么?
轻量级的开源的J2EE框架。
它是一个容器框架,用来装javabean(java对象),中间层框架(万能胶) 可以起一个连接作用,比如说把Struts和hibernate粘合在一起运用,可以让我们的企业开发更快、更简 洁
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
--从大小与开销两方面而言Spring都是轻量级的。
--通过控制反转(IoC)的技术达到松耦合的目的 --提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的 开发
--包含并管理应用对象(Bean)的配置和生命周期,这个意义上是一个容器。
--将简单的组件配置、组合成为复杂的应用,这个意义上是一个框架。
系统是由许多不同的组件所组成的,每一个组件各负责一块特定功能。除了实现自身核心功能之外,这 些组件还经常承担着额外的职责。例如日志、事务管理和安全这样的核心服务经常融入到自身具有核心 业务逻辑的组件中去。这些系统服务经常被称为横切关注点,因为它们会跨越系统的多个组件。
当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从 上到下的关系,但并不适合定义从左到右的关系。例如日志功能。
日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。
在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
AOP:将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象 (具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增 强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情
容器概念、控制反转、依赖注入
ioc容器:实际上就是个map(key,value),里面存的是各种对象(在xml里配置的bean节点、 @repository、@service、@controller、@component),在项目启动的时候会读取配置文件里面的 bean节点,根据全限定类名使用反射创建对象放到map里、扫描到打上上述注解的类还是通过反射创 建对象放到mp里。
这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,再通过DI注入 (autowired、resource等注解,xml里bean节点内的ref属性,项目启动的时候会读取xml节点ref属性 根据id注入,也会扫描这些注解,根据类型或id注入;id就是对象名)。
控制反转:
没有引入IOC容器之前,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须 主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。
引入IOC容器之后,对象A与对象B之间失去了直接联系,当对象A运行到需要对象B的时候,IOC容器会 主动创建一个对象B注入到对象A需要的地方。
通过前后的对比,不难看出来:对象A获得依赖对象B的过程,由主动行为变为了被动行为,控制权颠倒 过来了,这就是“控制反转”这个名称的由来。
全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一 种类似“粘合剂”的作用,把系统中的所有对象粘合在一起发挥作用,如果没有这个“粘合剂”,对象与对 象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
依赖注入:
“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器 主动注入。依赖注入是实现IOC的方法,就是由IOC容器在运行期间,动态地将某种依赖关系注入到对 象之中。
ApplicationContext是BeanFactory的子接口 ApplicationContext提供了更完整的功能:
①继承MessageSource,因此支持国际化。
②统一的资源文件访问方式。
③提供在监听器中注册bean的事件。
④同时加载多个配置文件。
⑤载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用 getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问 题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法 才会抛出异常。
ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我 们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。
ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的 时候,你就不用等待,因为它们已经创建好了。
相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置 Bean较多时,程序启动较慢。
BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用 ContextLoader。
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的 使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
我们知道对于普通的 Java 对象来说,它们的生命周期就是:
实例化
该对象不再被使用时通过垃圾回收机制进行回收
而对于 Spring Bean 的生命周期来说:
创建前准备
实例化 Instantiation
属性赋值 Populate
初始化 Initialization
销毁 Destruction
实例化 -> 属性赋值 -> 初始化 -> 销毁
1、解析类得到BeanDefinition
2、如果有多个构造方法,则要推断构造方法
3、确定好构造方法后,进行实例化得到一个对象
4、对对象中的加了@Autowired注解的属性进行属性填充
5、回调Aware方法,比如BeanNameAware,BeanFactoryAware
6、调用BeanPostProcessor的初始化前的方法
7、调用初始化方法
8、调用BeanPostProcessor的初始化后的方法,在这里会进行AOP
9、如果当前创建的bean是单例的则会把bean放入单例池
10、使用bean
11、Spring容器关闭时调用DisposableBean中destory()方法
singleton:默认,每个容器中只有一个bean实例,单例的模式由beanfactory自身来维护。该对象的生命周期与spring IOC容器一致
prototype:为每一个bean请求提供一个实例。在每次注入时都会创建一个新的对象
request:bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用 这一个单例对象。
session(会话)
global-session(全局会话)
spring是一个IOC容器,用来管理Bean,使用依赖注入实现控制反转,可以很方便的整合各种框架,提 供AOP机制弥补OOP的代码重复问题、更方便将不同类不同方法中的共同处理抽取成切面、自动注入给 方法执行,比如日志、异常等
springmvc是spring对web框架的一个解决方案,提供了一个总的前端控制器Servlet,用来接收请求, 然后定义了一套路由策略(url到handle的映射)及适配执行handle,将handle结果使用视图解析技术 生成视图展现给前端
springboot是spring提供的一个快速开发工具包,让程序员能更方便、更快速的开发spring+springmvc 应用,简化了配置(约定了默认配置),整合了一系列的解决方案(starter机制)、redis、 mongodb、es,可以开箱即用
1. 用户发送请求到前端控制器(DispatcherServlet)
2. 前 端 控 制 器 ( DispatcherServlet ) 收 到 请 求 调 用 处 理 器 映 射 器 (HandlerMapping),去查找处理器(Handler)
3. 处理器映射器(HandlerMapping)找到具体的处理器(可以根据 xml 配置、注解进行 查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet。
4. 前端控制器(DispatcherServlet)调用处理器映射器(HandlerMapping)
5. 处理器适配器(HandlerAdapter)去调用自定义的处理器类(Controller,也叫 后端控制器)。
6.自定义的处理器类(Controller,也叫后端控制器)将得到的参数进行处理并返回结 果给处理器映射器(HandlerMapping)
7. 处 理 器 适 配 器 ( HandlerAdapter ) 将 得 到 的 结 果 返 回 给 前 端 控 制 器 (DispatcherServlet)
8. DispatcherServlet( 前 端 控 制 器 ) 将 ModelAndView 传 给 视 图 解 析 器 (ViewReslover)
9. 视图解析器(ViewReslover)将得到的参数从逻辑视图转换为物理视图并返回给 前端控制器(DispatcherServlet)
10. 前端控制器(DispatcherServlet)调用物理视图进行渲染并返回
11. 前端控制器(DispatcherServlet)将渲染后的结果返回