线程池:
理解:线程池就是一个池子,他是空的,当有任务要执行时,我们就创建线程对象,当任务执行完,线程归还为线程池,节约资源,不用每次需要执行任务的时候就单独创建线程对象。
线程的状态
new:创建未启动
runnable:正在执行
blocking:阻塞状态
sleep:休眠状态
wating:等待状态
timed-waiting:----sleep–
terminated:线程结束
默认的线程池:
Api提供了一个工具类Executors,来创建线程池
1、Executors.newCachedThreadPool()—默认存储int类型最大长度的线程数量
2、Executors.newFixedThreadPool(int)----最大参数大小的线程数量
3、Executors.newSingleThreadPool()—单个线程数量
4、Executors.newScheduledThreadPool(int)----和2差不多 但是可以指定延迟
线程池的使用步骤:创建----submit(lambda表达式)提交任务—shutdown-终结任务
自定义线程池:ThreadPoolExecutor创建线程池:
new ThreadPoolExcutor(
1、核心线程,
2、最大线程数量
3、空闲时间
4、空闲时间单位–TimeUnit.SECENDS
5、阻塞队列
6、默认工程对象,帮我们自动生成线程
7、当等待的线程超过 最大线程数量+任务队列的数量时–使用何种方式拒绝线程任务
)
拒绝策略:
1、new ThreadPollExecutor.AbortPolicy()—抛出异常
2、new ThreadPoolExecutor.CallerRunsPolicy()----多的任务给其他线程处理
3、new ThreadPoolExecutor.DiscardOldestPolicy()—放弃旧的线程任务
4、new ThreadPoolExecutor.DiscardPolicy —直接丢去多的任务
volatile关键字
强制线程每次在使用共享数据时都会去看一下共享数据
synchronized 同步代码块也是一样
但是volatile不能实现原子性,只能实现数据共享更新
但是synchronized可以实现原子性,因为一个里面的操作锁起来了,其他的线程不能抢占当前的执行权
原子性
整个操作过程中不会被线程调度器中断,就可以认为是原子性
可以理解—就一个操作—a =1 就是原子性
count++;就不是 因为他分为三步 得到原先的值—加1----赋值
AtomicInteger类可以保证原子性:自旋锁+CAS算法原理分析:
CAS:内存的值、旧值、要改变的值
线程1先抢到执行权,就会判断旧值6和内存的值6是否相等-相等就将改变的值7赋值给主内存里面的值然后线程2抢到执行权,拿自己的旧值6和主内存的值7比较,不相等就自旋,重新将主内存的值赋值给自己的旧值,再拿自己的旧值7和主内存的值7比较,相等就将自己的新值8赋值给主内存的值
线程安全相关类
数据结构:
ArrayList:不安全、效率高
Vector:安全、效率低(synchronized)
链表结构
HashMap:不安全、效率高
Hashtable:安全、效率低(synchronized)
StringBuilder:效率高、不安全
StringBuffer:效率低、安全
重点
ConcurrentHashMap:安全且效率相对高
结构:哈希表 线程安全–synchronized+CAS
jdk1.7版本
上图!
大数组Segment:不会扩容、长度为16、扩容因子0.75
还会创建一个小的数组:HashEntry[]:默认长度为2,在大数组的0索引位置作为小数组的模板
当调用put方法时:
1、先通过键的哈希值找到在大数组中对应的索引位置
如果值为null:按照模板创建小数组,二次哈希找到在小数组中对应的位置,直接存入
如果值不为null–二次哈希找到在小数组中应该存入的位置,
需要扩容就扩大数组的两倍
不需要扩容就判断改位置有没有元素
有就根据equals判断元素的属性是否相同
相同—丢弃
不相同–哈希桶结构
线程安全和效率高的体现:效率高–synchronized不会再大数组上面,是在每一个索引在执行存储元素时 安全—当下一个线程也在改索引位置时,就得等待
jdk1.8版本
原理相对于1.7版本要简单一些:
步骤:
1、使用put方法添加元素
2、计算当前元素应该存入的索引值
3、如果为null,使用cas算法,将这个节点存入改索引位置
4、如果不为null,用volatile方法得到改位置最新的节点地址,将该元素挂在该节点下
5、如果链表长度超过8,就变成红黑树结构
6、synchronized是在每一个索引起始节点上,这样就保证了多线程操作时数据的安全性
CountDownLatch
new CountDownLatch(number)
countDown()—调用一次number就-1
await():等待其他线程执行结束执行 即number为0时执行
Semaphore类
new Semaphore(number)
管理者控制线程数量
arquire()---- 线程数量减1
release()-----线程数量加1