万字图解多线程: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.局部变量是线程安全的
  1. open class DirtyReadObj {
  2. var username = "A"
  3. var password = "aaa"
  4. @Synchronized
  5. fun setUser(name: String, pwd: String) {
  6. this.username = name
  7. Thread.sleep(3000)
  8. this.password = pwd
  9. getUser()
  10. }
  11. fun getUser() {
  12. println(Thread.currentThread().name + ":user=$username, $password")
  13. }
  14. }
  15. class LockReinObj : DirtyReadObj() {
  16. companion object {
  17. @JvmStatic
  18. fun main(args: Array<String>) {
  19. var lockRein = LockReinObj()
  20. Thread(Runnable {
  21. lockRein.setGoodsBuyer(Goods()) //本同步方法执行完毕后,释放锁,其他线程可以再竞争该对象的同步资源
  22. println("能否释放锁?") //会的。
  23. lockRein.setUser("nfsq", "123456") //和t2线程竞争
  24. }, "t1").start()
  25. Thread(Runnable {
  26. lockRein.setUser("zgr", "zgr123")
  27. }, "t2").start()
  28. }
  29. }
  30. @Synchronized
  31. @Description("锁重入")
  32. fun setGoodsBuyer(goods: Goods) {
  33. println(Thread.currentThread().name + "已获得本对象的锁,在本同步方法调用setUser同步方法,并不需要释放锁才执行")
  34. setUser(goods.name, goods.price.toString())
  35. }
  36. }

II.synchronized 同步方法

脏读: 读取到无效数据 甚至对无效数据做操作

非线程安全就是多个线程对同一对象的变量同时并发访问,产生的结果就是脏读,读到不正确的数据。线程安全就是以获得实例变量的值是通过同步处理的,不会出现脏读 .

III. synchronized{}同步代码块的优势

synchronized使用的对象监控器是同一个。也就是说,当一个线程的同步方法被调用时,对象被锁定,该对象所有同步内容的访问被阻塞,其他线程无法访问

  1. 可以提高效率(锁定更细节)
  2. 可以为开发者不能修改代码加锁(如jar包类方法)

IV. Volatile

Volatile的任用: 保持内存可见性和防止指令重排

V. 读写锁

深入理解读写锁—ReadWriteLock源码分析

第三章 线程间通信

1. while实现线程通信

//线程二需要while轮询 才能实现线程间通信,对cup消耗很大
//线程主动读取 而非对象等待/通知

  1. fun main(args: Array<String>) {
  2. val goods = Goods()
  3. Thread(Runnable { goods.deduction(); Thread.sleep(300); goods.deduction(); }).start()
  4. Thread(Runnable {
  5. do {
  6. Thread.sleep(200)
  7. } while (goods.stock > 3)
  8. println("商品库存减少到小于4")
  9. }).start()
  10. }

2. wait和Notify

  • 谁wait对谁上锁. 在调用wait()和notify()之前,线程必须获得该对象的对象级别锁,即只能在(该对象的)同步方法或同步代码块(该对象)中调用wait方法。
  • 每个对象的notify()只能唤醒一个wait().如果存在同一对象的多个wait()线程,则随机唤醒一个
  • wait()触发后,wait线程被挂起,等到该对象notify()方法触发,wait线程回到可运行状态,与其他线程公平竞争。notify()方法触发后,必须等到对象级别锁释放,也就是sychronized(obj)代码块执行完毕 ,wait线程才能被唤醒.
  • wait()触发,wait线程放弃对象锁并挂起该线程。
  1. class WaN{
  2. private val lock = java.lang.Object()
  3. private val goods =Goods()
  4. fun dec(){
  5. synchronized(lock){
  6. for (i in 1..3) {
  7. goods.deduction()
  8. }
  9. lock.wait()
  10. //notify触发以后,线程被唤醒,但wait线程仍然要与其他线程公平竞争
  11. Thread.sleep(200)
  12. println("wait结束...")
  13. }
  14. }
  15. fun dis(){
  16. synchronized(lock){
  17. lock.notify()
  18. Thread.sleep(500)
  19. goods.discount()
  20. //wait的对象级别锁 的notify执行到这里才算结束,也就是说,必须使wait的对象级别锁释放,wait线程才能从挂起状态被唤醒,
  21. //而不是lock.notify行代码执行后,就立即唤醒wait线程
  22. }
  23. }
  24. companion object {
  25. @JvmStatic
  26. fun main(args: Array<String>) {
  27. val WaN = WaN()
  28. Thread(Runnable { WaN.dec() }).start()
  29. Thread(Runnable { WaN.dec() }).start()
  30. Thread.sleep(2000)
  31. Thread(Runnable { WaN.dis() }).start()
  32. }
  33. }
  34. }

3. 线程状态切换

多线程基础 - 图1

  1. private val lock = java.lang.Object();
  2. private val goods = Goods()
  3. class ThreadStateTask() : Runnable {
  4. override fun run() {
  5. Thread.sleep(300);
  6. synchronized(lock) {
  7. lock.wait()
  8. Thread.sleep(100)
  9. }
  10. val t2 = Thread(Runnable { Thread.sleep(100); })
  11. t2.start()
  12. t2.join()
  13. synchronized(goods) {
  14. goods.deduction()
  15. }
  16. while (true) {
  17. }
  18. }
  19. }
  20. fun main(args: Array<String>) {
  21. val task = ThreadStateTask()
  22. val t1 = Thread(task)
  23. println(t1.state.name) //NEW
  24. t1.start()
  25. println(t1.state.name) //就绪状态 RUNNABLE
  26. Thread.sleep(200)
  27. println(t1.state.name) //TIMED_WAITING sleep挂起有等待时间的wati
  28. Thread.sleep(400)
  29. println(t1.state.name)//WAITING 等待对象锁
  30. synchronized(lock) {
  31. lock.notify()
  32. }
  33. println(t1.state.name) //BLOCKED 阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
  34. Thread.sleep(150)
  35. println(t1.state.name) //WAITING join也是waiting
  36. Thread.sleep(300)
  37. val t2 = Thread(Runnable {
  38. synchronized(goods) {
  39. goods.discount()
  40. }
  41. })
  42. t2.start()
  43. Thread.sleep(50)
  44. println(t2.state.name) //BLOCKED 线程2处在goods对象锁的阻塞队列,等待线程1释放goods的对象锁
  45. Thread.sleep(1500)
  46. println(t1.state.name) //RUNNABLE t1线程在运行死循环代码,此时是running状态
  47. }

每个锁对象都有两个队列:一个是就绪队列,一个是阻塞队列。就绪队列了将要获得锁的线程,阻塞队列存储被阻塞的线程。