万字图解多线程: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"@Synchronizedfun setUser(name: String, pwd: String) {this.username = nameThread.sleep(3000)this.password = pwdgetUser()}fun getUser() {println(Thread.currentThread().name + ":user=$username, $password")}}class LockReinObj : DirtyReadObj() {companion object {@JvmStaticfun 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 {@JvmStaticfun 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) //NEWt1.start()println(t1.state.name) //就绪状态 RUNNABLEThread.sleep(200)println(t1.state.name) //TIMED_WAITING sleep挂起有等待时间的watiThread.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也是waitingThread.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状态}
每个锁对象都有两个队列:一个是就绪队列,一个是阻塞队列。就绪队列了将要获得锁的线程,阻塞队列存储被阻塞的线程。
