L o g o Java 线程 中软培训中心
内容概述 了解什么是线程 定义线程 实例化线程 启动线程 同步代码 了解线程的 4 个状态之间的转换关系 专题 继承 Thread 和和实现 Runnable 的比较 线程之间的关系
本章目标 学完以后我们应该可以 : 了解什么是线程 编程定义线程 编程实例化线程 知道如何启动线程 会使用同步代码来保护资源 说出线程的 4 个状态之间的转换关系
程序 进程和线程 程序是计算机指令的集合. 程序是一组静态的指令集, 不占用系统运行资源, 不能被系统调度, 也不能作为独立运行的单位, 它以文件的形式存储在磁盘上 进程 : 是一个程序在其自身的地址空间中的一次执行活动 比如, 打开一个记事本, 就是调用了一个进程 进程是资源申请 调度和独立运行的单位, 因此, 它使用系统中的运行资源 ; 而程序不能申请系统资源, 一个程序可以对应多个进程 线程又称为轻量级进程, 是进程中的一个单一的连续控制流程 它和进程一样拥有独立的执行控制, 由操作系统负责调度, 区别在于线程没有独立的存储空间, 而是和所属进程中的其它线程共享一个存储空间, 这使得线程间的通信远较进程简单一个进程可以拥有多个线程
多线程 多线程优点 可以更好的实现并行 恰当地使用线程时, 可以降低开发和维护的开销, 并且能够提高复杂应用的性能 CPU 在线程之间开关时的开销远比进程要少得多 因开关线程都在同一地址空间内, 只需要修改线程控制表或队列, 不涉及地址空间和其他工作 创建和撤销线程的开销较之进程要少 多线程使用的情形 : 前台处理用户请求, 后台有程序在跑. 我们看一下 API 对多线程有个大概的了解
Java 对多线程的支持 Java 在语言级提供了对多线程程序设计的支持 多线程操作会增加程序的执行效率 各线程之间切换执行, 时间比较短, 看似是多线程同时运行, 但对于执行者 CPU 来说, 某一个时刻只有一个线程在运行
实现多线程程序的两种方式 实现多线程程序的两种方式 : 从 Thread 类继承 ; 实现 Runnable 接口 唯一的一个要实现的方法是 void run() 通过完成 Runnable 接口, 实现一个线程类
启动线程 调用线程的 start() 方法.
run Run 可理解为是线程的入口函数 多线程的业务逻辑都写在 run 里
如何定义线程 继承 java.lang.thread 特点 : 不能再次扩展其他类 实现 :java.lang.runnable 无论是继承 Thread 类还是实现 Runnable 接口 都需要实现 run() 方法.
继承 java.lang.thread 先看一个继承 Thread 的实例 : Public class FirstThread extends Thread{ } Public void run(){ } System.out.println(); 写一个基本的线程实例 BaseThread.java
实现 Runnable 再看一个实现 Runnable 的实例 : class TestThread implements Runnable { public void run(){ } } System.out.println(); 基本的 Runable 示例 BaseRunable.java
练习 练习使用两种方式启动线程
多个线程的启动 如何启动多个线程呢? 一个线程上调用多次 start 是否可以实现呢?
实例化线程 继承 Thread 方式的实例化 写多线程的实例 ( 三个线程循环分别输出不同的内容 ) 例 :MoreThread.java FirstThread.java 实现 Runnable 方式的实例化 ( 三个线程循环分别输出不同的内容 ) 例 :MoreRunnable.java 写多个线程访问共有变量 i 的实例 RunableTest.java
练习 用线程实现火车票的售卖, 让 2 个窗口同时卖 100 张票 TrainBooking.java
设置守护线程 当 Java 虚拟机启动时, 通常都会有单个非守护线程 ( 它通常会调用某个指定类的 main 方法 ) 将线程标记为守护线程或用户线程 当正在运行的线程都是守护线程时,Java 虚拟机退出 SetDeamon.java
线程的停止 一般情况下我们不必要控制线程的停止 但是如果非要停止一个线程怎么办呢? Thread 类中带的线程停止的函数已经不推荐使用了 一般情况下是设置标识变量来控制 --StopThreadTest.java
线程有相应的优先级 static int MAX_PRIORITY 线程可以具有的最高优先级 static int MIN_PRIORITY 线程可以具有的最低优先级 static int NORM_PRIORITY 分配给线程的默认优先级 ThreadTestPriority.java
线程状态之间的转换 等待 / 阻塞 新线程 就绪态 运行态 结束
线程状态 新线程 : 当利用 new 关键字创建线程对象实例后, 它仅仅作为一个对象实例存在,JVM 没有为其分配 CPU 时间片和其他线程运行资源 ; 就绪状态 : 在处于创建状态的线程中调用 start 方法将线程的状态转换为就绪状态 这时, 线程已经得到除 CPU 时间之外的其它系统资源, 只等 JVM 的线程调度器按照线程的优先级对该线程进行调度, 从而使该线程拥有能够获得 CPU 时间片的机会 运行态 : 就绪态的线程获得 cpu 就进入运行态 等待 / 阻塞 : 线程运行过程中被剥夺资源或者, 等待某些事件就进入等待 / 阻塞状态,suspend() 方法被调用,sleep() 方法被调用, 线程使用 wait() 来等待条件变量 ; 线程处于 I/O 等待等, 调用 suspend 方法将线程的状态转换为挂起状态 这时, 线程将释放占用的所有资源, 但是并不释放锁, 所以容易引发死锁, 直至应用程序调用 resume 方法恢复线程运行 等待事件结束或者得到足够的资源就进入就绪态死亡状态 : 当线程体运行结束或者调用线程对象的 stop 方法后线程将终止运行, 由 JVM 收回线程占用的资源
sleep Sleep 是暂时释放 CPU 控制权, 把自己挂起, 因为只有一个 CPU, 多线程是属于抢占式的 作用 : 强制线程回到可运行 ( 就绪 ) 状态, 使其他线程能够有机会执行, 保证一个线程不会一直执行
sleep 需要注意的事项 sleep 没有释放锁 不可以调用别的线程的 sleep 方法 sleep 必须要进行 try,catch 处理 不可以使别的线程进入 sleep 状态 TestSleep.java
同步 我们先看个程序 SynThread.java 这个程序有什么问题?
同步 在单位时间只有一个线程执行特定的代码
synchronized 在 java 中使用 synchronized 实现同步 synchronized 获得对象锁 Synchronized 会降低程序的性能 实现同步有两种方法 同步代码块 --SynThreadBlock.java 同步函数 --SynThreadMethod.java
线程状态之间的转换 再来理解一遍 : 等待 / 阻塞 新线程 就绪态 运行态 结束
专题 继承 Thread 和实现 Runnable 的比较 当 extends Thread 后就不能在继承其他的类了 实现 Runnable 后还可以 extends 别的类并实现其他的接口 extends Thread 后具有多个对象的实例 implements Runnable 具有单个对象的实例
专题 -- 线程之间的关系 线程之间的关系 join() yield() 线程之间的交互 wait() notify() notifyall()
join 某个线程加到另一个线程的 尾部, 当该线程执行完后另一个线程再继续执行. thread.join(); JoinTest.java
yield 当前运行的线程暂时退出运行状态, 会到可运行状态, 使同级别的线程有机会执行 但 yield 不能保证任何事情 Thread.yield(); --YieldTest.java
程序之间的交互 主要涉及到的方法 wait() notify() notifyall() Wait 和 notify 方法要在同步块中调用
wait 相关知识点 调用 wait 必须拥有对象上的锁 wait 可以释放锁 可以调用别的 Thread 的 wait 方法 wait 和 notify 必须要成对出现
wait 实例 注意该程序较难理解 看一个程序 waittest.java
小结 现在我们应该可以 : 了解多线程的概念 可以编程定义和实例化和启动线程 可以说出 sleep 的作用和知识点 知道实现同步有两种方式 编程实现同步代码来保护资源 (synchronized) 可以说出线程的各个状态之间的转换关系 可以说出继承 Thread 和实现 Runnable 的区别并编写代码进行体现 了解 wait 的相关知识点
作业 用线程模拟售票员的售票过程 要求 : 多个售票员不能同时出售某张票