实现多线程的三种方式
继承Thread类 实现Runnerable或 Callable接口(实现Callable接口的类的对象要传入FutureTask对象的构造方法,将futureTask对象传给Thread对象)
线程的优先级是1-10 默认是5
单机解决多线程问题,可以使用synchronized 或 lock接口 (ReentrantLock实现类) lock() unlock(),多机器涉及同步并发的时候就需要引入分布式锁
死锁 多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行 原因:资源有限 同步嵌套
ArrayBlockingQueue: 底层是数组,有界
LinkedBlockingQueue: 底层是链表,无界.但不是真正的无界,最大为int的最大值
BlockingQueue的核心方法:
put(anObject): 将参数放入队列,如果放不进去会阻塞
take(): 取出第一个数据,取不到会阻塞
ThreadPoolExecutor 线程池 线程池最多可执行的任务数 = 队列容量 + 最大线程数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize: 核心线程的最大值,不能小于0
maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
keepAliveTime: 空闲线程最大存活时间,不能小于0
unit: 时间单位
workQueue: 任务队列,不能为null
threadFactory: 创建线程工厂,不能为null
handler: 任务的拒绝策略,不能为null
/**
* 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 ,
TimeUnit.SECONDS ,
new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new
ThreadPoolExecutor.DiscardPolicy()) ;
num++ 这个动作并不是原子性的,不是原子性的操作,在多线程的情况下会出现线程安全问题 AtomicInteger这是原子性的类
num++这个动作实际分成三步 1.从共享数据中读取数据到本线程栈中 2.修改本线程栈中变量副本的值 3把本线程栈中变量副本的值赋值给共享数据 执行每一步后都可能被打断
volatile关键字 只能保证每次使用共享数据的时候是最新值,但是不能保证原子性 如果不加这个关键字,线程默认使用的线程栈内的副本数据
synchronized 关键字 也会强制去查看最新的共享数据,所以也可以解决线程栈内数据不是最新数据的问题
AtomicInteger 原理 自旋锁 + CAS算法
cas算法: (内存值,旧的预期值,要改的值)
如果内存值与旧的预期值相同,则将内存值修改为要改的值.不同则重新获取内存值作为旧的预期值(自旋),要改的值为现在的预期值+增量值,此时再去比较内存值和旧的预期值是否相同,相同则修改
synchronized和cas都可以实现线程安全,但是synchronized总是从最坏的角度出发,认为每次获取共享数据都有可能被别的线程修改,所以每次修改共享数据都上锁.这是悲观锁.
而cas从乐观的角度出发,认为获取的共享数据不会被别的线程修改,只是自己在修改共享数据的时候确认下是否有变化,如果没变化直接修改,有变化那就再次获取共享数据.所以叫乐观锁.
ConcurrentHashMap jdk1.7 最多16个线程同时访问
1.数组默认长度是16 加载因子0.75 ,数组名为segment .这个大数组一旦创建无法扩容 多线程访问的时候只锁hash表的一个索引,不影响其它的
2.再创建一个长度为2的小数组,把地址赋值给0索引,其它索引内容都是null
3.进来的数据经过hash应放在16个索引之一,索引值为null则创建一个长度为2的数组,然后二次hash看存在小数组的哪个索引.
ConcurrentHashMap jdk1.8
空参构造什么都不做,在第一次添加元素的时候才进行初始化 这个hash表是可以扩容的 每个链表大于8个会转为红黑树 多线程访问的时候只锁hash表的一个索引,不影响其它的
CountDownlatch 一个线程需要等待其它线程执行完毕再执行的情况下使用 new CountDownlatch(3) 构造函数指定需要等待几个线程,底层是初始化一个计数器 await()线程等待,计数器为0时被唤醒 countDown()计数器减一
Semaphore 对某一特定资源限定访问线程数量 acquire()获取锁 release()释放锁 new Semaphore(2) 构造函数指定最多允许几个线程