• 1. Java高级应用编程 —— 多线程编程V1.1
  • 2. 本章内容节知识点掌握程度难易程度多线程简介进程/线程区别掌握多线程实现的两种方式继承掌握接口掌握两种实现方式比较掌握线程的属性和控制线程状态及其生命周期掌握线程类中的主要方法掌握线程优先级掌握线程休眠和线程中断掌握线程的高级操作掌握多线程的同步/通信问题线程同步掌握难锁掌握难死锁理解
  • 3. 多线程简介进程 计算机在执行的程序的实体 e.g. 一个.class文件 一个.exe文件 …… 线程 一个程序内部的顺序控制流一个进程中可以包含 一个或多个线程,一个 线程就是一个程序内部 的一条执行线索 基本概念
  • 4. 进程/线程区别 进程和线程的区别 多进程和多线程 示例 HostAParty.java 每个进程都有独立的代码和数据空间,进程的切换 会有很大的开销 同一类线程共享代码和数据空间,每个线程有独立 运行的栈和程序计数器,线程切换的开销小多进程:在操作系统中能同时运行多个任务(程序) 多线程:在同一应用程序中有多个顺序流同时执行
  • 5. 多线程实现的两种方式多线程的实现 (1) 创建线程类 继承Thread类 或 实现Runnable接口 (2) 通过Thread类构造器来创建线程对象 Thread( ) Thread(Runnable target) (3) 通过start()方法激活线程对象 线程运行
  • 6. 多线程实现的两种方式创建线程的两种方式 — 线程类 继承Thread类 —— java.lang.Thread 实现Runnable接口 —— java.lang.Runnable run( )方法 — 线程运行体 要将一段代码(线程体)在一个新的线程上运行,该代码应该在一个线程类的run( )函数中 写一个类implements Runnable接口,且必须覆盖Runnable接口中的run( )方法 写一个类extends Thread类,且必须重写Thread类的run( )方法
  • 7. 继承Thread类实现线程类线程类线程调用 定义线程类继承Thread类 覆盖run( )方法 public void run( ) 继承Thread类ThreadDemo.javaTestThread.java
  • 8. 继承Thread类实现线程类run( ) 与 start( )TestThread.java
  • 9. 实现Runnable接口实现线程类线程类线程调用 定义线程类实现Runnable接口 线程共享同样 的数据和代码 覆盖Runnable接口中的唯一的方法 public void run( ) 实现Runnable接口RunnableDemo.javaTestRunnable.java
  • 10. 两种实现方式的比较两种方式的比较 使用Runnable接口可以避免由于JAVA的单继承性带来的局限 适合多个相同的程序代码的线程去处理同一资源情况,把线程同程序的代码、数据有效的分离 推荐使用实现Runnable接口 示例TicketRunnable.javaTicketThread.javaTestTicket.java
  • 11. 线程状态及其生命周期线程的状态及其生命周期 一个 Thread 对象在它的整个生存期中能以几种不同的状态存在 start( ) — 方法使线程处于可以运行的状态,但 不一定意味着该线程立即开始运行
  • 12. 包含等待的线程状态线程状态及其生命周期
  • 13. 线程类中的主要方法线程中的主要方法
  • 14. 线程的优先级线程的优先级 Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照线程的优先级来决定应调度哪个线程来执行 Java线程的优先级用1~10的整数来表示,越小则优先级越低 但是Java的优先级是高度依赖于操作系统的实现的 Thread类的三个常量,表示常用的线程优先级: Thread.MIN_PRIORITY //1 Thread.NORM_PRIORITY // 5 Thread.MAX_PRIORITY // 10 缺省时线程具有NORM_PRIORITY 线程优先级方法 getPriority( ) —— 确定线程的优先级 setPriority( ) —— 设置线程的优先级T1.javaT2.javaTestPriority.java示例
  • 15. 线程的休眠线程的休眠sleep( ) 让线程中止一段时间的静态方法 Thread.sleep(long millis) — 暂时停止执行millis毫秒 在睡眠期满的瞬间,再次调用该线程不一定会恢复它的执行 join() 导致当前线程等待,直到调用这个 join 方法的线程终止 join( ) join(long millis) join(long millis,int nanos)yield() 为其他可运行的线程提供执行机会 静态方法 — Thread.yield( )Joining.javaTestJoin.javaYieldThread.javaTestYield.javasleeping.javaTestSleep.java
  • 16. 线程的中断线程的终止 自动终止 — 一个线程完成执行后,不能再次运行 手动终止 stop( ) —— 已过时,基本不用 interrupt( ) —— 粗暴的终止方式 可通过使用一个标志指示 run 方法退出,从而终止线程 —— 推荐使用
  • 17. 线程的高级操作线程的高级操作 Object类中线程的相关方法: void wait() 导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll() 方法。 void notify() 唤醒在此对象监视器上等待的单个线程。 void notifyAll() 唤醒在此对象监视器上等待的所有线程。
  • 18. 同一票号被打印两次 打印出0,甚至负数票号 线程同步售票程序 线程安全Ticket.javaTestSyn.java解决方案 — 线程同步
  • 19. 线程同步有时两个或多个线程可能会试图同时访问一个资源 例如,一个线程可能尝试从一个文件中读取数据,而另一个线程则尝试在同一文件中修改数据 在此情况下,数据可能会变得不一致 为了确保在任何时间点一个共享的资源只被一个线程使用,使用了“同步” 当一个线程运行到需要同步的语句后,CPU不去执行其他线程中的、可能影响当前线程中的下一句代码的执行结果的代码块,必须等到下一句执行完后才能去执行其他线程中的相关代码块,这就是线程同步
  • 20. 对象的锁标志 每个对象都有一个锁标志 使用synchronized可与锁标志交互 synchronized关键字的用法有两种: synchronized语句 synchronized方法 synchronized语句: synchronized (obj){ //在synchronized语句块中 //对obj的成员只能进行同步访问 } 锁
  • 21. synchronized (Object) { //要同步的语句 } 锁 synchronized void methodA() { }synchronized方法synchronized语句 实现同步的两种方式 一旦一个包含同步方法(用synchronized修饰)的线程被CPU调用,其他线程就无法调用相同对象的同步方法。当一个线程在一个同步方法内部,所有试图调用该方法的同实例的其他线程必须等待要取得锁的对象
  • 22. 锁实现同步的两种方式 — synchronized方法使用同步方法未使用同步TicketSynMethod.javaTestSynMethod.java示例:
  • 23. 锁实现同步的两种方式—synchronized代码块使用同步代码块TicketSynCode.javaTestSynCode.java示例:
  • 24. 锁实现同步的两种方式 注意事项: 受到synchronized保护的程序代码块和方法中,要访问的对象属性必须设定为private,因为如果不设定为private,那么就可以用不同的方式来访问它,这样就达不到保护的效果了
  • 25. 锁 优点: 可以显示的知道哪些方法是 被synchronized关键字保护的 缺点: 方法中有些内容是不需要同步的,如果该方法执行会花很长时间,那么其他人就要花较多时间等待锁被归还; 只能取得自己对象的锁,有时候程序设计的需求,可能会需要取得其他对象的锁; synchronized代码块 实现同步的两种方式—两种方式的优缺点synchronized方法 优点: 可以针对某段程序代码同步,不需要浪费时间在别的程序代码上; 可以取得不同对象的锁; 缺点: 无法显示的得知哪些方法 是被synchronized关键字保护的;
  • 26. 死锁死锁 两个线程,彼此在等待对方占据的锁 锁的归还几种方式: 基本上执行完同步的程序代码后,锁就会自动归还; 用break语句跳出同步的语句块,不过这对于写在方法声明的synchronized没有作用; 遇到return语句; 遇到了异常;DeadLockThread_1.javaDeadLockThread_2.javaTestDeadLock.java
  • 27. 本章重点总结多线程简介 多线程模型 多线程实现的两种方式 实现Runnable接口 继承Thread类 线程的属性和控制 线程状态及其生命周期 线程类的主要方法 休眠和中断 多线程同步/通信问题 线程同步,锁和死锁
  • 28. 课后作业1、利用Thread实现,要求多线程求解某范围素数每个线程负责1000范围:线程1找1-1000;线程 2 找 1001-2000;线程 3 找2001-3000。编程程序将每个线程找到的素数及时打印。 [必做题]   2、利用Runnable实现,要求多线程求解某范围素数每个线程负责1000范围:线程1找1-1000;线程 2 找 1001-2000;线程 3 找2001-3000。编程程序将每个线程找到的素数及时打印。 [必做题]
  • 29. 课后作业3、编写一个Java程序(包括一个主程序类,一个线程类。在主程序类中创建2个线程,将其中一个线程的优先级设为10,另一个线程的优先级设为6。让优先级为10的线程打印200次“线程1正在运行”,优先级为6的线程打印200次“线程2正在运行”。 [选做题] 4、编写一个计时器,每隔一秒钟,在控制台打印出最新时间。 [必做题]