资源预览内容
第1页 / 共30页
第2页 / 共30页
第3页 / 共30页
第4页 / 共30页
第5页 / 共30页
第6页 / 共30页
第7页 / 共30页
第8页 / 共30页
第9页 / 共30页
第10页 / 共30页
亲,该文档总共30页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
Java程序设计基础与实践,主编 费雅洁 中国水利水电出版社,第10章 多线程编程,本章导读,并发操作经常发生,例如,利用电脑听音乐时可以同时打印文档,Java提供多线程编程实现并发操作。多线程与并发处理是程序设计好坏优劣的重要标志。 Java编写程序都运行在JVM中,在JVM内部,每用Java命令启动一个Java应用程序,就会启动一个JVM进程。在同一个JVM进程中,有且只有一个进程,所有程序代码的运行都是以线程来运行。对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。因此,线程间的通信很容易,速度也很快。 但并发操作有时可能会出现问题,例如,老师修改学生成绩时,学生可能正在查询成绩,同一时间一个人往仓库中加货,另一个人从仓库中取货,这都是实际中经常发生的事情,如果控制不好就会出现学生查询到的成绩不是老师修改后的成绩,仓库中货物的总量统计不正确等问题,所以需要对并发操作进行控制。 在Java程序中,JVM负责线程的调度。,本章要点, Java线程类和接口 Java线程的创建方法 Java线程的生命周期 Java线程的优先权 Java线程的同步和死锁,10.1 基础知识,10.1.1 什么是多线程? 现在的操作系统是多任务操作系统,多线程是实现多任务的一种方式。进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,显然,程序是死的、静态的,进程是活的、动态的。在Windows下,进程又被细化为线程,也就是一个进程下有多个能独立运行的更小的单位。在Windows中,CPU分配是以线程为单位的,一个进程可由多个线程组成。通过在一个程序进程内部同时执行多个线程,可以充分利用CPU等系统资源,从而最大限度的提高系统响应速度。 对于操作系统而言,其调度单元是线程。一个进程至少包括一个线程,通常将该线程称为主线程。一个进程从主线程的执行开始进而创建一个或多个附加线程,就是所谓基于多线程的多任务。 多线程编程是程序设计技术中的一个很重要的领域,目前多数主流的操作系统都支持多任务操作。多线程是进行大型复杂软件系统开发的一把利器。,10.1.2 Thread类和Runnable接口,Java中编程实现多线程有两种方法,一是创建用户自己的线程子类,二是在用户自己的类中实现Runnable接口。无论哪种方法都需要使用Java基础类库中的Thread类及其方法。, Thread类,Thread是Java的线程类,该类定义了线程所有的基本操作。, Runnable接口,Runnable接口的定义如下: public interface Java.lang.Runnable public abstract void run(); 由此可见Ruunnable接口中只有一个抽象方法run(),所有实现了Runnable接口的类都必须实现run()方法。Runnable接口中的run()方法可以被运行时系统自动识别和执行,即当线程被调度并转入运行状态时,系统自动执行run()方法中的代码。,10.1.2 创建线程, 通过Thread类创建线程 Thread类提供了一个public void run()方法,该方法不执行任何操作,是线程的执行体。因此,Thread的子类应该重写该方法,以便于重定义线程的执行体。 使用Thread类创建线程要遵循以下步骤: 通过extendsThread类创建用户自己的线程; 重写run()方法; 创建用户自己线程类的对象并且调用start()方法使之进入就绪队列,等待运行。,class TwoThreadsTest public static void main (String args) new SimpleThread(“线程1“).start();/创建用户自己线程类的对象,调用start()方法 new SimpleThread(“线程2“).start();/创建用户自己线程类的对象,调用start()方法 class SimpleThread extends Thread /定义用户自己的线程类 public SimpleThread(String str) /定义自己的构造方法 super(str); public void run() /重写run()方法 for (int i = 0; i 10; i+) /循环方式调用线程 System.out.println(i + “ “ + getName();/利用Thread类的getName()方法得到线程名字 try sleep(int)(Math.random() * 1000); catch (InterruptedException e) System.out.println(“DONE! “ + getName(); ,10.1.2 创建线程, 通过Runnable接口创建线程 通过Runnable接口创建线程的步骤如下: 通过implements Runnable接口创建用户自己的线程; 重写run()方法; 通过Thread()的构造方法创建线程类对象并且调用start()方法使之进入就绪队列,等待运行。,class TwoThreadsTest public static void main (String args) /通过Thread()构造方法创建用户自己线程类的对象,调用start()方法 new Thread(new SimpleThread(“线程1“).start(); new Thread(new SimpleThread(“线程2“).start(); class SimpleThread implements Runnable/implements Runnable接口 String name=“;/定义并获取线程名字 public SimpleThread(String str) /定义自己的构造方法 this.name=str; public void run() for (int i = 0; i 10; i+) System.out.println(i + “ “ + name); try Thread.sleep(int)(Math.random() * 1000); catch (InterruptedException e) System.out.println(“DONE! “ + name); , 两种方法创建线程的不同,Thread和Runnable实现线程是不同的。采用实现Runnable接口创建线程首先应定义一个实现Runnable接口的类,然后,把这个类的实例传给Thread构造方法。这样,新创建的Thread对象共同拥有一个Runnable实例,共享数据;采用继承Thread类的方法创建线程,实际上创建了不同的Thread对象,它们不共享数据。,class MyThread extends Thread public int x = 0; public void run() System.out.println(+x); class R implements Runnable private int x = 0; public void run() System.out.println(+x); public class Test public static void main(String args) for(int i=0;i10;i+) Thread t = new MyThread(); t.start(); R r = new R(); for(int i=0;i10;i+) Thread t = new Thread(r); t.start(); ,10.1.3 线程的生命周期,在Java中,线程是对象。作为一个可以独立运行的对象,线程具有从出生到死亡完整的生命周期,包括新建、就绪、运行、阻塞、死亡五个状态。 新建 当线程对象在通过new运算符被创建时,这个线程就处于新建状态。处于新建状态的线程,获得了自己运行所需的内存空间,但是却并不能被运行,因为处于新建状态的线程不会获得CPU资源。 就绪 为了能够获得CPU资源,必须通过线程对象调用start()方法,使线程对象进入就绪队列,等待CPU资源。处于就绪队列的线程被称为处于就绪状态。 JVM管理着一个线程的就绪队列,CPU会从就绪队列中获取线程并加以执行。线程一旦进入了就绪队列,就完全脱离了程序员的控制,在JVM的调度下运行,直至运行完毕。 运行 处于就绪队列中的线程,当获得CPU资源时,就会跳出就绪列表,开始执行run()方法中的语句,进入运行状态。可以通过Thread类提供的isAlive()方法得知线程是否处于运行状态。,10.1.3 线程的生命周期, 阻塞 处于运行状态的线程,在某些情况下会进入阻塞状态。比如,线程要进行读写磁盘的操作,而这个操作相对来说比较慢,这种情况下线程应该阻塞,以便让其他的线程得以执行。处于阻塞状态的线程不会被CPU运行,只有重新进入就绪列表排队等待CPU资源。 在以下情况下,线程会被阻塞: 当前线程已经运行了足够长的时间,JVM将CPU资源轮转给了其他线程,则当前线程进入就绪队列,等待再次被执行。 当前线程在运行过程中,主动调用sleep(int millsecond)方法,Thread类的sleep()方法能够让当前执行的线程睡眠。在睡眠期间,线程会主动放弃CPU的使用权,所以其他的线程可以获取到CPU使用权继续运行。睡眠指定millsecond时间后线程自然醒来,重新进入就绪队列排队等候CPU资源。 在线程睡眠时候是可以由其他的线程调用某线程的interrupt()方法将该线程强行唤醒的,被吵醒的线程会抛出InterruptedException异常。在Java中,有时候抛出异常并不等于有错误发生,这里就是一个例子。这里抛出异常只是用于向系统说明,线程是被吵醒的,而不是自然醒来的。线程被吵醒后,重新进入就绪队列排队等候CPU资源。 当前线程在运行过程中,主动调用了wait()方法进入阻塞状态,直到其他线程调用notify()或者notifyAll()方法将它唤醒,然后重新进入就绪列表,排队等待被再次运行。,10.1.3 线程的生命周期, 死亡 所谓线程的死亡状态是指线程对象释放掉了自己的实体部分,即释放掉了分配给线程对象的内存。 线程的死亡原因分为“正常死亡”和“非正常死亡”两种。正常死亡是指线程执行了run()方法中的所有代码,完成了执行任务。这种情况下,线程会被系统释放掉。非正常死亡是指线程还没有完成自己的执行任务,被人为调用了stop()方法强制性地杀死了。由于强制杀死一个线程会引起一些安全性的问题,因此在新版的JDK中,该方法已经被建议不再使用。,public class MultiThread public static void main(String args) System.out.println(“我是主线程!“); /下面创建线程实例thread1 ThreadUseExtends thread1=new ThreadUseExtends(); /创建thread2时以实现了Runnable接口的THhreadUseRunnable类实例为参数 Thread thread2=new Thread(new ThreadUseRunnable(),“SecondThread“); thread1.start();/启动线程thre
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号