万字图解多线程:https://my.oschina.net/u/3829664/blog/4550613?utm_source=osc_group_backend
第一章 多线程基础
Thread类核心方法
join()
执行线程等待被调用线程执行完毕,才执行下步操作
yield()
当前线程让出cpu时间片,让其他任务执行
数据被多个线程共享 - 扣减库存问题
stop()
stop强制线程代码中断,对象放弃锁(导致数据不同步)
suspend和resume
suspend独占 :当一个线程的同步方法在执行过程中,线程被挂起,则该线程长期独占对象的所有同步方法直到resume(),这很容易引起死锁
其他
线程的优先级
- 继承特性 线程A启动线程B,则B线程的优先级与A是一样的
- 规则性 高优先的线程总是大部分先执行完(但不一定)
- 随机性 未必高优先的就一定先执行
守护线程
Dameon(保姆工作) 如GC 其他用户线程都执行完毕后,守护线程和系统一块退出
第二章 同步锁
Synchronzied两个重要特性:互斥性和可见性
I. synchronized 同步
1. 同步锁是对象锁
无法同步代码块还是同步方法,都是对对象上锁。或者说是对同一主内存数据上锁。
2. 锁重入
某一线程访问某对象的同步方法,该线程仍然可以访问该对象的其他同步方法(其他线程不可以访问该对象的任何同步内容),不需要等待释放对象锁
3. 同步块局部上锁提高效率
synchronized(非this)与非this同步内容异步执行, 不会争抢this锁(同步方法和对this的同步代码块),极大提高运行效率
4. 同步无法被继承
子类override父方法,仍然需要添加synchronized关键字
5.抛出异常,同步锁释放
6.局部变量是线程安全的
open class DirtyReadObj {
var username = "A"
var password = "aaa"
@Synchronized
fun setUser(name: String, pwd: String) {
this.username = name
Thread.sleep(3000)
this.password = pwd
getUser()
}
fun getUser() {
println(Thread.currentThread().name + ":user=$username, $password")
}
}
class LockReinObj : DirtyReadObj() {
companion object {
@JvmStatic
fun main(args: Array<String>) {
var lockRein = LockReinObj()
Thread(Runnable {
lockRein.setGoodsBuyer(Goods()) //本同步方法执行完毕后,释放锁,其他线程可以再竞争该对象的同步资源
println("能否释放锁?") //会的。
lockRein.setUser("nfsq", "123456") //和t2线程竞争
}, "t1").start()
Thread(Runnable {
lockRein.setUser("zgr", "zgr123")
}, "t2").start()
}
}
@Synchronized
@Description("锁重入")
fun setGoodsBuyer(goods: Goods) {
println(Thread.currentThread().name + "已获得本对象的锁,在本同步方法调用setUser同步方法,并不需要释放锁才执行")
setUser(goods.name, goods.price.toString())
}
}
II.synchronized 同步方法
脏读: 读取到无效数据 甚至对无效数据做操作
非线程安全就是多个线程对同一对象的变量同时并发访问,产生的结果就是脏读,读到不正确的数据。线程安全就是以获得实例变量的值是通过同步处理的,不会出现脏读 .
III. synchronized{}同步代码块的优势
synchronized使用的对象监控器是同一个。也就是说,当一个线程的同步方法被调用时,对象被锁定,该对象所有同步内容的访问被阻塞,其他线程无法访问
- 可以提高效率(锁定更细节)
- 可以为开发者不能修改代码加锁(如jar包类方法)
IV. Volatile
Volatile的任用: 保持内存可见性和防止指令重排
V. 读写锁
深入理解读写锁—ReadWriteLock源码分析
第三章 线程间通信
1. while实现线程通信
//线程二需要while轮询 才能实现线程间通信,对cup消耗很大
//线程主动读取 而非对象等待/通知
fun main(args: Array<String>) {
val goods = Goods()
Thread(Runnable { goods.deduction(); Thread.sleep(300); goods.deduction(); }).start()
Thread(Runnable {
do {
Thread.sleep(200)
} while (goods.stock > 3)
println("商品库存减少到小于4")
}).start()
}
2. wait和Notify
- 谁wait对谁上锁. 在调用wait()和notify()之前,线程必须获得该对象的对象级别锁,即只能在(该对象的)同步方法或同步代码块(该对象)中调用wait方法。
- 每个对象的notify()只能唤醒一个wait().如果存在同一对象的多个wait()线程,则随机唤醒一个
- wait()触发后,wait线程被挂起,等到该对象notify()方法触发,wait线程回到可运行状态,与其他线程公平竞争。notify()方法触发后,必须等到对象级别锁释放,也就是sychronized(obj)代码块执行完毕 ,wait线程才能被唤醒.
- wait()触发,wait线程放弃对象锁并挂起该线程。
class WaN{
private val lock = java.lang.Object()
private val goods =Goods()
fun dec(){
synchronized(lock){
for (i in 1..3) {
goods.deduction()
}
lock.wait()
//notify触发以后,线程被唤醒,但wait线程仍然要与其他线程公平竞争
Thread.sleep(200)
println("wait结束...")
}
}
fun dis(){
synchronized(lock){
lock.notify()
Thread.sleep(500)
goods.discount()
//wait的对象级别锁 的notify执行到这里才算结束,也就是说,必须使wait的对象级别锁释放,wait线程才能从挂起状态被唤醒,
//而不是lock.notify行代码执行后,就立即唤醒wait线程
}
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
val WaN = WaN()
Thread(Runnable { WaN.dec() }).start()
Thread(Runnable { WaN.dec() }).start()
Thread.sleep(2000)
Thread(Runnable { WaN.dis() }).start()
}
}
}
3. 线程状态切换
private val lock = java.lang.Object();
private val goods = Goods()
class ThreadStateTask() : Runnable {
override fun run() {
Thread.sleep(300);
synchronized(lock) {
lock.wait()
Thread.sleep(100)
}
val t2 = Thread(Runnable { Thread.sleep(100); })
t2.start()
t2.join()
synchronized(goods) {
goods.deduction()
}
while (true) {
}
}
}
fun main(args: Array<String>) {
val task = ThreadStateTask()
val t1 = Thread(task)
println(t1.state.name) //NEW
t1.start()
println(t1.state.name) //就绪状态 RUNNABLE
Thread.sleep(200)
println(t1.state.name) //TIMED_WAITING sleep挂起有等待时间的wati
Thread.sleep(400)
println(t1.state.name)//WAITING 等待对象锁
synchronized(lock) {
lock.notify()
}
println(t1.state.name) //BLOCKED 阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
Thread.sleep(150)
println(t1.state.name) //WAITING join也是waiting
Thread.sleep(300)
val t2 = Thread(Runnable {
synchronized(goods) {
goods.discount()
}
})
t2.start()
Thread.sleep(50)
println(t2.state.name) //BLOCKED 线程2处在goods对象锁的阻塞队列,等待线程1释放goods的对象锁
Thread.sleep(1500)
println(t1.state.name) //RUNNABLE t1线程在运行死循环代码,此时是running状态
}
每个锁对象都有两个队列:一个是就绪队列,一个是阻塞队列。就绪队列了将要获得锁的线程,阻塞队列存储被阻塞的线程。