【Java】韩顺平Java学习笔记 第17章 多线程基础

第17章 多线程基础

基本概念

  • 程序:编写的指令集合,其实就是我们写的代码
  • 进程:运行中的程序
  • 线程
    • 单线程
    • 多线程
  • 并发
  • 并行

线程使用

继承 Thread 类

  • 重写 run 方法(来自 Runnable 接口,Thread 类是实现了 Runnable 接口的 run 方法),写上自己的业务逻辑
  • 当类继承 Thread 类,该类就可以当做线程
  • sleep 休眠方法(必须有异常处理)
  • start 启动线程方法,调用 run 方法(为什么不直接调用 run 方法?)
  • currentThread().getName() 获取当前线程
多线程机制
  1. 运行程序后,创建了这个程序的进程
  2. 进入主方法后,这个进程创建了一个主线程(main线程)
  3. 当主线程内再新建了一个继承 Thread 类的对象,这时主线程创建了一个子线程
  4. 子线程运行的同时,主线程不会阻塞,仍然继续执行
  5. 当主线程运行完后,会终止消失,但若主线程的子线程还没有执行完,子线程不会消失,而是继续执行。当所有线程结束后,进程才会结束
  6. 可以使用 Jconsole 监控线程执行情况
为什么是 start 方法?
  • 主线程直接调用 run 方法,则 run 方法会被当作一个普通的方法,主线程要执行完 run 方法后才会执行主线程剩余的代码,即主线程被阻塞。故直接调用 run 方法,没有真正地创建一个子线程,必须调用 start 方法
  • start 源码里会执行 start0 方法是本地方法(native方法),由 jvm 机和操作系统 调用
  • 调用 start0 方法后,只是将线程变成了可运行状态,具体什么时候上 CPU 执行,需要根据操作系统的调度算法
  • 调用 start 方法才会创建新线程,start 调用 run 方法不会创建新线程

实现 Runnable 接口(建议使用)

  • Java 是单继承的,如果一个类已经继承了其他类,这时只能直接实现 Runnable 接口
  • 直接实现时,没有 start 方法(不能直接调用 run 方法!)
  • 解决方式是创建一个 Thread 对象,将实现 Runnable 接口的对象放进去,再通过 Thread 对象调用 start 方法
    Thread thread = new Thread(dog);
  • 底层使用静态代理设计模式
  • 可以让两个线程来执行一个实现 Runnable 接口的对象,更适合多个线程共享一个资源的情况

线程终止

终止的两种方式

  • 线程完成任务时
  • 通过使用变量来控制 run 方法退出(修改线程类里的循环判断变量),即通知方式

线程常用方法

用户线程和守护线程

  • 用户线程
  • 守护线程(举例:垃圾回收机制)——怎么设置守护线程?setDaemon方法

常用方法

  • setName

  • getName

  • interrupt 中断线程,但并没有真正结束线程,只是进入休眠状态(sleep)

    如果线程正在休眠,则结束休眠(一般用法)

  • setPriority 设置优先级**(优先级一般用于进程调度算法)**

    • MIN_PRIORITY = 1(最低)
    • NORM_PRIORITY = 5(普通)
    • MAX_PRIORITY = 10(最高)
  • getPriority

  • start

  • run

  • sleep

  • yield 线程礼让(让出CPU让其他进程执行,但不保证成功,因为有时内核资源不紧张,CPU觉得没有必要礼让。且礼让时间不确定)

  • join 线程插队,即抢占(一旦插队成功,则肯定先执行完插入的线程的所有的任务,即使是主线程也暂时不执行)

线程的生命周期

线程状态
  • 官方文档有6种
    • New
    • Runnable(就绪态,可分为两个状态,这就是某些书上说7种状态的由来)
      • Ready
      • Running
    • Blocked(阻塞)
    • Waiting
    • TimedWating(超时等待)
    • Teminated(终止)
  • 某些书上说的是7种
  • 查看状态用 getState 方法

线程同步

  • 线程同步机制概念

Synchronized 关键字 —— 实现同步的具体方法

  • 同步代码块(建议选择,范围较小,效率尽可能高)

    synchronized (对象) { //得到对象的锁,才能操作同步代码
    //需要被同步的代码
    }
    
  • 放在方法声明种,表示为同步方法

    public synchronized void func(String name){
    	//需要被同步的代码
    }
    

互斥锁

  • 是一个标记
  • 用 synchronized 修饰时,拥有互斥锁
  • 同步局限性:效率降低
  • 非静态同步方法的锁可以加在 this(这个对象),也可以加在其他对象(要求是线程使用的是同一个对象、共享的对象,即这个其他对象来当锁,这个对象不能有很多个。如果有很多个,虽然锁住了,但是每个线程都能抢到锁,实际上相当于没锁住)
  • 静态同步方法的锁只能加在类本身(类名.class)
  • 同步方法如果没有使用static修饰:默认锁对象为this
  • 如果方法使用static修饰,默认锁对象:当前类.class
  • 注意多个线程的锁对象必须是同一个!

线程死锁

  • 死锁概念

释放锁

释放锁的情况
  • 当前线程的同步方法、同步代码块执行结束
  • 当前线程的同步方法、同步代码块遇到break、return
  • 当前线程的同步方法、同步代码块出现了未处理的Error或Exception,导致异常结束
  • 当前线程的同步方法、同步代码块执行了线程对象的wait()方法,当前线程暂停,并释放锁
不会释放锁的情况
  • 当前线程的同步方法、同步代码块调用 sleep、yield 方法

  • 执行同步代码块时,其它线程调用该线程的挂起方法 suspend(使其进入到就绪态)

    注:尽量避免使用 suspend 和 resume 方法,已不再推荐使用

  • 36
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值