线程篇
线程安全的定义
当一个类被多个线程进行访问并且正确运行,它就是线程安全的
线程的分类
用户线程/守护线程
线程实现方式:
继承Thread类 。
实现Runnable接口 。
实现callable接口
应用程序可以使用Executor框架来创建线程池 。
Executors提供四种线程池
分别为:
线程池名称 | 说明 |
newCachedThreadPool | 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 |
newFixedThreadPool | 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。定长线程池的大小最好根据系统资源进行设置。 |
newScheduledThreadPool | 支持定时及周期性任务执行。 |
newSingleThreadExecutor | 创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务 |
关闭线程:
Shutdown:空闲的线程interrupt,之前提交的任务可以继续执行/
shutdownNow:大部分都会被interrupt
线程池
线程池原理
线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。
线程池特点:
线程复用; 控制最大并发数; 管理线程
线程池组成
a线程管理器/
b工作线程/
c 任务接口/
d 任务队列/提供一种缓冲机制
线程池参数:
1 corePoolSize,线程池里核心线程数
2 maximumPoolSize,线程池里最大线程数量,超过最大线程时候会使用RejectedExecutionHandler
3 keepAliveTime, 空闲线程存活时间
4 unit,空闲线程存活时间单位
5 workerQueue,缓存异步任务的队列
6 threadFactory,用来构造线程池里的worker线程
7 handler拒绝策略
[
策略名称 | 说明 | |
Abort | Policy | 直接抛出异常,默认策略 |
Caller-Runs | 用调用者所在的线程来执行任务 | |
DiscardOldest | 丢弃阻塞队列中靠最前的任务,并执行当前任务 | |
Discard | 直接丢弃任务 |
]
线程池方法
execute():提交任务,交给线程池执行
submit():提交任务,能够返回执行结果 execute + Future
【
execute与submit的区别:
】
shutdown():关闭线程池,等待任务都执行完
shutdownNow():关闭线程池,不等待任务执行完
getTaskCount():线程池已执行和未执行的任务总数
getCompletedTaskCount():已完成的任务数量
getPoolSize():线程池当前的线程数量
getActiveCount():当前线程池中正在执行任务的线程数量
线程的生命周期
// java.lang.Thread.State 引入后,点击State,即可看到源码
Java中的线程有6种状态分别是:
新建(new),/
运行(RUNNABLE),/
阻塞(BLOCKED)、/
[
等待阻塞(wait)/同步阻塞(lock)/其他阻塞(sleep|join)
]
等待(WAITING),/
等待超时(TIMED_WAITING),/
终止(TERMINATED)/
使用线程池 的好处
1>避免频繁地创建和销毁线程,达到线程对象的重用。
2>使用线程池还可以根据项目灵活地控制并发的数目。
线程的一些基本方法
Wait/ notify/notifyAll/ sleep/ join/yield/isAlive/activeCount/enumerate(枚举程序中的线程)/currentThread/
isDeamon/setDeamon/setName/setpriority(设置一个线程的优先级)/getpriority/countDownLatch(线程计数器)/CyclicBarrier/Semaphore(信号量)
方法比较:
1>wait和sleep
1.1>Wait=object类方法/Sleep=Thread类方法
1.2>最大的不同是在等待时wait会释放锁,而sleep一直持有锁。
1.3>Wait通常被用于线程间交互,sleep通常被用于暂停执行。
Join()/此方法可以让线程按顺序执行
LockSupport
当对象的等待队列中有多个线程时,notify只能随机选择一个线程唤醒,无法唤醒指定的线程
2>快速失败(fail-fast)和安全失败(fail-safe)
快速失败:当你在迭代一个集合的时候,如果有另一个线程正在修改你正在访问的那个集合时,就会抛出一个ConcurrentModification异常。
在java.util包下的都是快速失败。
安全失败:你在迭代的时候会去底层集合做一个拷贝,所以你在修改上层集合的时候是不会受影响的,不会抛出ConcurrentModification异常。
在java.util.concurrent包下的全是安全失败的。
3>synchronized和Lock
主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。
3.1>Lock是一个类,synchronized是一个关键字
3.2>synchronized会自动释放锁,
Lock一定要求程序员手工释放,并且必须在finally从句中释放
4>Runnable和Callable的区别:
1>产生时间[Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的
]
2>方法区别[Callable规定的方法是call(),Runnable规定的方法是run()
]
1>返回值区别[Callable的任务执行后可返回值,而Runnable的任务是不能返回值(是void)]
2>异常区别[call方法可以抛出异常,run方法不可以]
3>运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
6>加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。
5>synchronized和reentrantlock区别
1>实现方式不同[Synchronized是依赖于JVM实现的,而ReenTrantLock是JDK实现的]
2>针对锁不同[ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁]
3>唤醒区别[ReenTrantLock提供了一个Condition(条件)类,用来实现分组唤醒需要唤醒的线程们,而不是像synchronized要么随机唤醒一个线程要么唤醒全部线程]
4>独特性[ReenTrantLock提供了一种能够中断等待锁的线程的机制,通过lock.lockInterruptibly()来实现这个机制]
Lock/unlock-try/finally结合
Yield方法可以暂停当前正在执行的线程对象,让其它有相同优先级的线程执行
线程的关键字
Volatile
特征:变量可变性/禁止重排序
使用场景:一个变量被多个线程共享
ThreadLocal:
定义:线程本地变量
使用场景: 数据库连接、 Session 管理等
ThreadLocalMap
ConcurrentHashMap == 分段锁
性能 | 使用比率 | 安全方面 | 修饰范围 | 是否阻塞 | 解决内容 | |
Volatile(V) | 轻量级实现, V>S | S>V | volatile能保证数据的可见性,但不能保证原子性; synchronized可以保证原子性,也可以间接保证可见性 | 变量 | 不会 | 变量在多个线程之间的可见性 |
Synchronized(S) | 方法 | 会 | 多个线程之间访问资源的同步性 |
线程的终止方式
1 正常运行结束
2 使用退出标识推出线程
3 interrupt结束 线程
4 使用stop方法结束线程
锁
对比对象 | 定义 | 应用场景 | 不足 | 实现 |
悲观锁 | 只允许一个线程访问 | 多写 |
|
|
乐观锁 | 允许多个线程防范 | 多读 | 1循环时间长开销大 2只能保证一个共享变量的原子操作 | 1>cas算法 2>数据库:版本号机制 |
[
ABA问题的根本在于cas在修改变量的时候,无法记录变量的状态,比如修改的次数,否修改过这个变量。这样就很容易在一个线程将A修改成B时,另一个线程又会把B修改成A,造成casd多次执行的问题
]
>>死锁
定义:多个并发进程因争夺系统资源而产生相互等待的现象
避免死锁的方式:
1固定加锁的顺序(针对锁顺序死锁)
2开放调用(针对对象之间协作造成的死锁)
3使用定时锁-->tryLock()
>>自旋锁
短时间内释放资源,用户线程和内核的切换的消耗
>>同步锁
synchronized 它可以把任意一个非 NULL 的对象当作锁。 他属于独占式的悲观锁,同时属于可重入锁
>>ReentrantLock
默认为非公平锁。
可以唤醒指定条件的线程。
>>ReadWriteLock 读写锁
>>独占锁
>>共享锁
>>重量级锁
>>轻量级锁
>>偏向锁
>>分段锁
>>优化锁
[
-Xss参数用来控制线程的堆栈大小
在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程拥有某个具体对象的锁。
异常没有被捕获该线程将会停止执行
信号量
]