MYSQL基础详解:https://www.cnblogs.com/itchang/p/5784863.html
事物隔离性详解:https://www.cnblogs.com/fjdingsd/p/5273008.html
阿里JAVA开发手册:http://kangroo.gitee.io/ajcg/#/naming-style
Java关键字final、static使用总结:http://blog.51cto.com/lavasoft/18771
--------------------------------------------------------------------------------------------------
多线程
进程:进程是线程的集合,正在运行的应用程序。
线程:线程是一条执行路径,一个独立的执行单元。
多线程作用:调高程序效率。
创建线程的方法:
1:集成Thread类
2:实现Runnable接口
3:匿名内部类
4:callable
5:线程池
同步:代码从上往下执行,又称:单线程
异步:每个线程之间互不影响,又称:多线程
守护线程:和主线程,GC线程一起销毁
非守护线程:主线程销毁了,还是会继续执行的
进程中包括了,主线程,GC线程,用户线程(用户自己创建的线程,非守护线程)
多线程运行状态:新建状态 - 就绪状态 - 运行状态 - 阻塞状态 - 死亡状态
多线程运行状态流程图:https://img-blog.csdn.net/20161017181631639?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center
线程安全问题:当多个线程共享同一个全局、静态变量,做写操作时,可能会受到其他线程的干扰,导致数据冲突的问题呢
解决线程安全问题:
1:同步synchroized -- 自动释放
2:锁(locak) -- 手动释放
为什么使用同步或者锁能解决线程安全问题?
多个线程共享同一个全局、静态变量时,只让一个线程的代码执行,代码执行完毕后释放锁,在让其他线程执行。
这里需要注意一下,
同步和线程同步不是一个概念
程序中的同步是单线程的,代码从上往下执行
线程同步是保证线程安全
同步synchroized执行方法
1:同步代码块 -- 使用的是对象锁
synchroized(锁){}
2:同步函数 -- 使用的是this锁
public synchroized void add(){}
3: 静态同步函数 -- 使用的是当前字节码文件锁(类名.class)
public static synchroized void add(){}
注意:在多线程的同步中使用的不是同一把锁,那么线程还是不安全的
同步synchroized必须有的条件
1:必须要有两个线程以上,需要发生同步
2:多个线程想同步,必须用同一把锁
3:保证只有一个线程进行执行
同步synchroized原理
1:有一个线程已经拿到锁了,其他线程会一直等待,直到线程释放了锁,其他线程将会抢这把锁,抢到锁的线程将会执行代码。
同步synchroized缺点:效率低
死锁:同步中嵌套同步,无法释放,一直等待,变成死锁。
多线程三大特性
原子性:在操作中要么都执行,要么都不执行,保证数据一致
可见性:java内存模型,一个线程修改了共享变量值,其他线程能过立即得知这个修改后的值
有序性:程序执行的顺序按照代码的先后顺序执行
java内存模型(JMM):一个线程对共享变量写入是,能对别的线程可见
流程图:https://sfault-image.b0.upaiyun.com/140/667/1406676357-594a0dc7e13e8_articlex
java内存模式
主内存:主要存放共享全局变量
私有本地内存(工作内存):本地线程私有变量
看流程图就知道为什么数据会不可见了。
线程1从主内存中获取到共享变量(A)到工作内存1中,然后在修改共享变量(A)的值,在修改期间,线程2从主内存中获取到共享变量(A)到工作内存2中(这里需要注意的是,线程1还没修改值,线程2获取到的是旧的值),线程1修改完后将共享变量(A)传递给主内存中,这就发生了可见行的问题。
加上volatile就可以解决可见性问题:private static volatile int A;
volatile的作用:保证线程之间可见性,不保存原子性
AtomicInteger的作用:保证原子性
多线程通讯方式:https://static.oschina.net/uploads/space/2018/0125/120221_Ronq_3578766.png
wait():让当前线程从运行状态变成休眠状态,释放锁的资源
notify(): 让单钱线程从休眠的状态变成运行状态
注意,在同步中才能使用wait()、notify()
join():等待主线程执行完毕
Lock锁
Lock锁的写法
Lock lock = new ReentrantLock();
lock.lock();
try{
//可能会出现线程安全的操作
}finally{
//一定在finally中释放锁
//也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
lock.ublock();
}
Lock锁与synchronized同步锁的区别
lock手动锁
synchronized同步自动锁
Condition用法
Condition的功能类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能。
线程安全的类
Vector和ArrayList区别
实现原理都是通过数据实现,查询速度快,增加,修改,删除速度慢
区别:线程安全问题
Vector是安全(上锁的集合),ArrayList线程不安全
ArrayList效率高
Hashtable和HasMap集合
Hashtable线程是安全
HasMap线程是不安全
链表+数组 put Hascode取模得到下标位置 一致性取模算法
证明:Hashtable是线程安全?查看源码的put方法
JDK并发包
前言:
在JDK1.5之后,发明一种新并发包
JDK1.5之后,产生了很多java并发包
Hashtable线程是安全,效率非常低,锁的资源竞争。
多线程共享同一个hashtable 加锁、影响效率、每次只能有个线程去操作hashtable
并发安全类
ConcurrenHashMap -- 并发包
分段锁 默认16段
将一个整体(集合)拆分成多个小的Hashtable,默认分成16段
这样就可以有效的减少锁的资源竞争
CountDownLatch --计数用的
CyclicBarrier --计数用的
并发队列 --生产者消费者概念
在并发队列中分有界、无界
有界、无界的区别
为防止理解不深,先引用一个问题
数组和集合的区别
数组是有长度限制的、集合是没有长度限制的
同等
有界是有限制的,无界是无限制的
阻塞队列、非阻塞队列 --这里要注意是阻塞'队列'
区别:
生产者写入满的时候,就会进入到阻塞。
当队列为空的时候,消费者也会进入阻塞。
队列遵循的规则
先进先出
后进后出
ConcurrentLinkedDeque和BlockingQueue的区别:
BlockingQueue可以阻塞,有界。
ConcurrentLinkedDeque不可阻塞,无界。
多线程
什么是线程池
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序
都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,
还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用
线程池,必须对其实现原理了如指掌。
线程池作用
线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。
如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。
在线程池中 ThreadPoolExecutor 是核心类
线程池四种创建方式
Executor封装了四种线程池类型
Java通过Executors(jdk1.5并发包)提供四种线程池,分别为:
1,newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2,newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3,newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
4,newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
java中线程池 核心使用的构造函数 采用ThreadPoolExecutor
ThreadPoolExecutor构造函数参数
corePoolSize: 核心池的大小
maximumPoolSize: 线程池最大线程数
keepAliveTime: 终止时间
unit: 超时秒数
线程池原理
线程池原理剖析
提交一个任务到线程池中,线程池的处理流程如下:
1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。
2、线程池判断工作队列是否已满,如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。
3、判断线程池里的线程是否都处于工作状态,如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
线程池 合理配置 --
CPU密集 : 线程数和CPU数相同
CPU使用频繁就使用
IO密集:2*CPU核数
操作数据库,IO需要等待,线程都需要等待,阻塞
CPU密集和IO密集的区别
IO密集经常会有阻塞,休眠,CPU密集会频繁的调用
java锁机制
悲观锁
乐观锁
分段锁
重入锁
读写锁
CAS无锁
自旋锁
排它锁
案例:
买水果案例
小明买了10块水果,给了老板钱。然而小红不知道小明给了钱,小红又给了老板10块。(数据重复)
在“多数据源”的情况下(2个或者2个以上的JDBC连接)就要使用锁
什么是悲观锁?
默认加排它锁
每次在拿数据的时候,都会上锁。
悲观锁:悲观锁悲观的认为每一次操作都会造成更新丢失问题,在每次查询时加上排他锁。
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿 这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如 行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
使用方法:
SQL语句后面添加 for update
例子:
select * from order for update
原理:
只允许有一个连接进行操作,当一个线程进入后会锁住,别的线程要等待锁释放才能使用
缺点:
效率低 --因为只能保证一个连接进行操作
什么是乐观锁?
版本控制
乐观锁:乐观锁会乐观的认为每次查询都不会造成更新丢失,利用版本字段控制
原理:
首先数据库中有个版本号(version),2个数据源同时操作SQL语句,第一个操作的时候version是0,然后会把version+1(版本号+1),那么第二个操作的时候查询version=0的数据是没有的,查不到的,最后判断影响行数就可以了,假如影响行数>0就可以进行操作
悲观锁和乐观锁的区别
如果查询量小,可以使用悲观锁
使用版本控制操作,使用乐观锁
什么是重入锁?
重入锁(ReentrantLock 和synchronized) 与 非重入锁(死锁) 递归使用同步
什么是读写锁?
两个线程同时读一个资源没有任何问题,所以应该允许多个线程能在同时读取共享资源。但是如果有一个线程想去写这些共享资源,就不应该再有其它线程对该资源进行读或写(译者注:也就是说:读-读能共存,读-写不能共存,写-写不能共存)
什么是CAS无锁?
原子类底层如何实现保证线程安全,CAS无所机制效率比有所机制搞。
CAS无所机制其实和乐观锁概念类似
CAS体系中有三个参数
它包含三个参数CAS(V,E,N): V表示要更新的变量,E表示预期值,N表示新值。仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。
实现过程:
当V=E表示没有任何线程操作,那么N值改为V值,返回N
但V!=E表示有线程操作过,那么N值改为E值,返回V
例子:
AtomicInteger就是CAS无锁机制实现的
什么是自旋锁?
内部不停的进行循环