目录
线程(Thread)
线程的创建方式 实现方式
Runnable和Callable的区别
线程的命名和优先级
线程的六种状态
线程的插队
线程的中断
线程的让出
守护线程
设置线程为守护线程
sleep()和wait()的区别
线程的同步synchronized锁
语法格式
实现原理
锁升级(锁优化、所膨胀)
线程安全的案例
ReentrantLock锁
并发集合
CopyOnWriteArrayList
思想
并发修改时保证线程安全
并发读取
CopyOnWriteArraySet
BlockingQueue阻塞队列
ArrayBlockingQueue:有界队列
LinkedBlockingQueue:无界队列
ConcurrentHashMap
线程池
常用方法
执行流程
配置参数
拒绝策略
常用线程池
线程(Thread)
线程的创建方式 实现方式
创建方式只有一种:通过Thread创建
实现方式有四种分别是:
-
继承Thread类实现
-
传入Runnable接口实现类实现
-
传入Callable接口实现类实现(要用FutureTask转化为runnable)
-
通过线程池实现
Runnable和Callable的区别
-
runnable接口中的run()方法没有返回值,callable接口中的call()方法有返回值
-
callable接口实现类中的run方法允许异常向上抛出,可以在内部处理,runnable接口实现类中run方法的异常必须在内部处理,不能向上抛出。
线程的命名和优先级
命名:
-
在实例化Thread时传入线程名
-
setName()方法设置线程名
优先级:setPriority()方法设置,(最大10,最小1,默认5)
线程的六种状态
线程创建后为new状态,start()启动后进入runnable可运行状态,run()方法执行结束后进入terminated终止状态,runnable有三个分支状态,当多个线程竞争时,没有获取到锁的线程进入blocked阻塞状态。调用Object.wait()方法和Thread.join()方法后会进入waiting等待状态,调用notify()方法唤醒线程重新进入runnable状态。调用Thread.join(时间值)、Object.wait(时间值)、Thread.sleep(时间值)等方法进入timed_waiting计时,等待状态时间结束后恢复runnable可运行状态。
线程的插队
调用 join() 方法实现插队。
底层通过调用Object的wait方法实现,因为线程在die的时候会自动调用自身的notifyAll方法,来释放所有的资源和锁。
线程的中断
interrupt()
修改中断状态为true,抛出InterruptedException,执行结束。
线程的让出
yield() 让出一次
守护线程
设置线程为守护线程
通过 setDaemon(true) 方法设置
守护线程是用来为用户线程服务的,当一个程序中的所有用户线程都结束之后,无论守护线程是否在工作都会跟随用户线程一起结束。
sleep()和wait()的区别
-
休眠过程中,当前线程不会让出持有的"锁",等待过程中,当前线程会让出持有的"锁"
线程的同步synchronized锁
线程安全问题:多个线程同时访问竞争一个资源时,会导致数据损坏不一致。
保证一段代码的原子性就是通过加锁和解锁实现的,实现线程的同步安全,参数对象一致则使用同一个锁。
synchronized (mutex) {Counter.count-=1;}
synchronized是一个关键字
语法格式
代码块:自定义对象作为锁
普通方法前:this,代表调用该方法的对象
静态方法前:该静态类Class对象
实现原理
1.通过Monitorenter和Monitorexit两个指令实现
2.通过monitor监视器机制实现线程同步
WaitSet:线程等待区
EntryList:线程阻塞区
Owner:线程拥有者
锁升级(锁优化、所膨胀)
在JDK1.6之前,synchronized性能开销较大;在JDK1.6之后,对synchronized进行了优化,它会自动根据程序的执行情况,自动进行锁的升级:偏向锁->轻量级锁->重量级锁
偏向锁(偏斜锁):只有一个线程访问时,使用偏向锁,通过Owner记录线程ID实现
轻量级锁:出现多个线程访问时(没有并发),使用轻量级锁,通过CAS实现
重量级锁:出现多个线程并发访问时,使用重量级锁。由于重量级锁,使用操作系统的互斥锁实现。(使用互斥锁,从“用户态”切换至“内核态”,带来性能开销,所以性能相对较差)
线程安全的案例
可变字符串的线程安全: StringBuffer : 线程安全(在改变字符串内容的方法上使用synchronized同步锁),性能较差
StringBuilder:线程不安全,性能较好
集合类的线程安全(使用synchronized关键字实现线程安全)
List接口的线程安全实现类:Vector,Stack
Map接口的线程安全实现类:Hashtable
ReentrantLock锁
1.ReentrantLock是核心类库提供的锁实现类,实现了Lock接口,通过lock()方法加锁,unlock()方法释放锁。
2.通过trylock()方法支持获取锁的尝试机制
3.支持公平锁和非公平锁,内部通过AQS机制实现
ReentrantLock实现线程安全的案例
CopyOnWriteArrayList
ArrayBlockingQueue
并发集合
List Set Map Queue
CopyOnWriteArrayList
思想
修改时将原数组内容复制Copy到新数组内,在新数组内修改,然后替换
并发修改时保证线程安全
通过ReentrantLock实现多个线程并发修改时的线程安全同步(添加元素的同时,不允许删除)
添加新元素:list.add("")
按照指定下标替换元素:list.set(index, element)
按照指定下标删除元素:list.remove(0)
并发读取
没有加锁,允许多个线程同时并发读取;但是读取时,可能产生脏读(读取的同时,允许写入操作)。
CopyOnWriteArraySet
内部通过一个CopyOnWriteArrayList实现
BlockingQueue阻塞队列
阻塞队列:有两个线程,分别进行读写(task和put)操作;读取时,不允许写入,如果队列为空,则读取线程阻塞;写入时,不允许读取,如果队列已满,则写入线程阻塞;
经常用于生产消费场景
ArrayBlockingQueue:有界队列
LinkedBlockingQueue:无界队列
ConcurrentHashMap
JDK1,7:通过分段锁实现线程安全
JDK1,8:通过 synchronized+CAS实现线程安全
当产生哈希冲突时,通过synchronized将根节点作为锁,进行线程的同步安全
在没有产生哈希冲突时,通过CAS进行无锁化操作,降低synchronized进行线程同步操作所引发的性能下降
线程池
常用类和接口
ExecutorService接口:线程池的操作
Executors类:工具类,提供了常见线程池的封装
ThreadPoolExecutor类:具体线程池实现类
常用方法
void execute(Runnable command):提交线程任务
Future<T> submit(Callable<T> task):提交线程任务,可以获取线程执行结果
void shutdown():将线程池中的线程任务执行完毕后,关闭线程池
List<Runnable> shutdownNow():立刻关闭线程池,并返回未完成的线程任务
执行流程
-
提交线程任务,分配空闲线程
-
判断“工作线程数”是否超出“核心线程数”,如果未超出,则创建新线程;
-
如果超出,将线程任务存入工作队列
-
如果工作队列已满,判断判断“工作线程数”是否超出“最大线程数”,如果未超出,则创建新线程
-
如果超出,则执行拒绝策略
配置参数
核心线程数
最大线程数
线程存活时间
工作队列
线程工厂
拒绝策略
AbortPolicy(默认拒绝策略):丢弃当前线程任务,并抛出RejectedExecutionException
AbortPolicy:丢弃当前线程任务
DiscardOldestPolicy:丢弃工作队列中最早入队的线程任务
CallerRunsPolicy:由当前调用线程处理执行线程任务
常用线程池
Executors.newFixedThreadPool(3):固定数目的线程池
Executors.newCachedThreadPool():动态数目的线程池(线程被缓存,提供重复使用效率)
Executors.newSingleThreadExecutor():仅包含1个线程的线程池(将大量的线程任务保存至工作队列,然后按照提交顺序,用一条线程依次处理)
Executors.newScheduledThreadPool(5):可以按照时间进行调度执行任务的线程池