编程红宝书(珍藏版) ava 完金自学手册 第9章多线程编程 多线程是Java程序设计语言的一个亮点,它使用户可以很方便地 编写多线程程序,虽然编写多线程代码需要考虑诸如安全、死锁、 资源共享的问题,但是总体上讲Java在编写多线程程序上比其他 语言都要简洁 使用多线程最直接的例子是具有用户界面的程序。如果用户界面 上设计了一个按钮,一旦单击该按钮程序会自动在网络上搜索指 定数据,当然这个过程会持续一段时间。如果没有多线程实现技 术,就会出现用户界面无法控制的局面,即在网络数据搜索完之 前,用户界面根本不响应其他界面输入。整个界面象是静止在那 里而无法操作。而我们希望不管系统当前在完成什么任务,都允 许用户操作界面元素,如査询数据,完成其他信息的处理等。这 样就要求程序可以同时执行多个任务,响应用户的不同操作请求 对于用户而言就仿佛有多个处理器在为其工作。而在单处理器的 计算机上完成程序的多任务功能就需要多线程技术 多线程技术可以模拟多处理器的效果,对用户而言计算机同时完 成一个程序的多个任务,而实际上该机制使得计算机把CPU周期按 照一定策略分配给每一个线程,而高速的CP使得用户觉得计算机 在同时完成多个任务。 Q 机械工业出版社 China Machine Press
第9章 多线程编程 • 多线程是Java程序设计语言的一个亮点,它使用户可以很方便地 编写多线程程序,虽然编写多线程代码需要考虑诸如安全、死锁、 资源共享的问题,但是总体上讲Java在编写多线程程序上比其他 语言都要简洁。 • 使用多线程最直接的例子是具有用户界面的程序。如果用户界面 上设计了一个按钮,一旦单击该按钮程序会自动在网络上搜索指 定数据,当然这个过程会持续一段时间。如果没有多线程实现技 术,就会出现用户界面无法控制的局面,即在网络数据搜索完之 前,用户界面根本不响应其他界面输入。整个界面象是静止在那 里而无法操作。而我们希望不管系统当前在完成什么任务,都允 许用户操作界面元素,如查询数据,完成其他信息的处理等。这 样就要求程序可以同时执行多个任务,响应用户的不同操作请求。 对于用户而言就仿佛有多个处理器在为其工作。而在单处理器的 计算机上完成程序的多任务功能就需要多线程技术。 • 多线程技术可以模拟多处理器的效果,对用户而言计算机同时完 成一个程序的多个任务,而实际上该机制使得计算机把CPU周期按 照一定策略分配给每一个线程,而高速的CPU使得用户觉得计算机 在同时完成多个任务
编程红宝书(珍藏版) ava 完金自学手册 9.1线程概述 ·线程是操作系统的概念,线程也称之为轻量级进程 ( lightweight process LWP),是CPU的基本使用单元, 它的轻量级名称是和进程相关的。线程由线程ID、程序记 数器、寄存器和堆栈组成,多个线程可以共享代码段、数 据段和诸如打开的文件等的系统资源。而传统的进程其实 就是单线程控制程序,每个进程都有自己的代码段、数据 段和其他系统资源。这无疑使得每个进程管理更多的内容, 从而称为重量级进程。“轻量”是指线程没有独自的存储 空间,和同一个进程的多个线程共享存储空间 多线程和传统的单线程在程序设计上的最大区别是每个线 程独自运行,是彼此独立的指令流,造成线程之间的执行 是乱序的,所以线程的控制需要谨慎对待 下面分别详细介绍进程和线程的概念,如何创建线程、设 置线程的优先级、线程控制和线程同步等关键问 G想撼玩出
9.1 线程概述 • 线程是操作系统的概念,线程也称之为轻量级进程 (lightweight process LWP),是CPU的基本使用单元, 它的轻量级名称是和进程相关的。线程由线程ID、程序记 数器、寄存器和堆栈组成,多个线程可以共享代码段、数 据段和诸如打开的文件等的系统资源。而传统的进程其实 就是单线程控制程序,每个进程都有自己的代码段、数据 段和其他系统资源。这无疑使得每个进程管理更多的内容, 从而称为重量级进程。“轻量”是指线程没有独自的存储 空间,和同一个进程的多个线程共享存储空间。 • 多线程和传统的单线程在程序设计上的最大区别是每个线 程独自运行,是彼此独立的指令流,造成线程之间的执行 是乱序的,所以线程的控制需要谨慎对待。 • 下面分别详细介绍进程和线程的概念,如何创建线程、设 置线程的优先级、线程控制和线程同步等关键问题
编程红宝书(珍藏版) ava 完金自学手册 9.2创建线程 在学习线程前,一定要先了解Java的线程机制, 然后学习如何利用 Thread类实现多线程。Java的 多线程机制提供了两种方式实现多线程编程, 种是通过继java.long. Thread类来实现,一种 是通过实现 Runnable接口实现 Q想桃工业出隙社
9.2 创建线程 • 在学习线程前,一定要先了解Java的线程机制, 然后学习如何利用Thread类实现多线程。Java的 多线程机制提供了两种方式实现多线程编程,一 种是通过继承java.long.Thread类来实现,一种 是通过实现Runnable接口实现
编程红宝书(珍藏版) ava 完金自学手册 9.2.1继承 Thread类创建线程 Thread类是Java实现多线程的提供了简单的方法, Thread类已经具备了运 行多线程所需要的资源,用户只需要重载该类的run(方法,把需要使用 多线程运行的代码放入该方法。这样这些代码就可以和其他线程“同时” 存在。创建线程对象并用该对象调用 start(方法则线程开始运行, start(方法提供了启动线程和线程运行所需要的框架。 代码是一个例子,说明使用继承 Thread类实现多线程。每次new一个线程 都设置一个线程计数器,表明建立的线程数。整个程序启动3个线程,每 个线程会有9次输出,但是三个线程的建立并非顺序执行,而每个线程的9 次输出也不一定会顺序输出。如代码继承 Thread类实现多线程示例所示 Q想桃工业出隙社 继承Tad实现多线程程字执行结果
9.2.1 继承Thread类创建线程 • Thread类是Java实现多线程的提供了简单的方法,Thread类已经具备了运 行多线程所需要的资源,用户只需要重载该类的run()方法,把需要使用 多线程运行的代码放入该方法。这样这些代码就可以和其他线程“同时” 存在。创建线程对象并用该对象调用start()方法则线程开始运行, start()方法提供了启动线程和线程运行所需要的框架。 • 代码是一个例子,说明使用继承Thread类实现多线程。每次new一个线程 都设置一个线程计数器,表明建立的线程数。整个程序启动3个线程,每 个线程会有9次输出,但是三个线程的建立并非顺序执行,而每个线程的9 次输出也不一定会顺序输出。如代码继承Thread类实现多线程示例所示
编程红宝书(珍藏版) ava 完金自学手册 9.2.2实现 Runnable接口创建线程 ·Java提供了另一个有用的接口实现多线程编程。因为Java 不支持多继承,所以如果用户的类已经继承了一个类,而 又需要多线程机制的支持,此时继承 thread类就不现实了。 所以 Runnable接口在这种情况下就很实用 Runnable接口有唯一一个方法 run o,所以实现该接口时必 须自己定义该方法,提供多线程需要执行的代码。如果运 行通过实现 Runnable接口的多线程程序,则需要借助 Thread类,因为 Runnable接口没有提供任何东西支持多线 程,必须借助 Thead类的框架实现多线程,即通过类 Thread 的构造函数 public thread( Runnable target)来实现。代 码是通过继承 Runnable接口而实现多线程的例子。我们分 析和运行该程序,观察输岀结果就可以很好的理解其运用 Q想桃工业出隙社
9.2.2 实现Runnable接口创建线程 • Java提供了另一个有用的接口实现多线程编程。因为Java 不支持多继承,所以如果用户的类已经继承了一个类,而 又需要多线程机制的支持,此时继承thread类就不现实了。 所以Runnable接口在这种情况下就很实用。 • Runnable接口有唯一一个方法run(),所以实现该接口时必 须自己定义该方法,提供多线程需要执行的代码。如果运 行通过实现Runnable接口的多线程程序,则需要借助 Thread类,因为Runnable接口没有提供任何东西支持多线 程,必须借助Thead类的框架实现多线程,即通过类Thread 的构造函数public Thread(Runnable target)来实现。代 码是通过继承Runnable接口而实现多线程的例子。我们分 析和运行该程序,观察输出结果就可以很好的理解其运用
编程红宝书(珍藏版) ava 完金自学手册 9.3线程的状态 ·在Java中线程的执行过程稍微有些复杂,但线程对象创建 后并不不是立即执行,需要做些准备工作才有执行的权利, 而一旦抢占到CPU周期,则线程可以运行,但CPU周期结束 则线程必须暂时停止,或线程执行过程中的某个条件无法 满足时也会暂时停止,只有等待条件满足时才会继续执行, 最后从runO方法返回后,线程退出。可以看出线程的执行 过程中涉及一些状态,线程就在这些状态之间迁移。 做一点说明,Java规范中只定义了线程的四种状态,即新 建状态、可运行状态、阻塞状态和死亡状态。为了更清晰 的说明线程的状态变化过程,我们认为划分为五个状态更 好理解,这里把可运行状态( Runnable)分解为就绪状态 和运行状态,可以更好的理解可运行状态的含义。 线程包括五个状态:新建状态、就绪状态、运行状态、阻 塞状态和死亡状态。下面分别详细介绍这五种状态 G想撼玩出
9.3 线程的状态 • 在Java中线程的执行过程稍微有些复杂,但线程对象创建 后并不不是立即执行,需要做些准备工作才有执行的权利, 而一旦抢占到CPU周期,则线程可以运行,但CPU周期结束 则线程必须暂时停止,或线程执行过程中的某个条件无法 满足时也会暂时停止,只有等待条件满足时才会继续执行, 最后从run()方法返回后,线程退出。可以看出线程的执行 过程中涉及一些状态,线程就在这些状态之间迁移。 • 做一点说明,Java规范中只定义了线程的四种状态,即新 建状态、可运行状态、阻塞状态和死亡状态。为了更清晰 的说明线程的状态变化过程,我们认为划分为五个状态更 好理解,这里把可运行状态(Runnable)分解为就绪状态 和运行状态,可以更好的理解可运行状态的含义。 • 线程包括五个状态:新建状态、就绪状态、运行状态、阻 塞状态和死亡状态。下面分别详细介绍这五种状态
编程红宝书(珍藏版) ava 完金自学手册 9.4线程的优先级 线程的有限级表示一个线程被CPU执行的机会多少。注意 这里用“机会多少”来表达而不是用“先后顺序”来表达。 在Java中虽然定义了设置线程优先级高低的方法,但是优 先级低并不意味着在不同优先级的线程中就不会被执行, 优先级低只说明该线程被执行的概率小,同理优先级高的 线程获得CPU的概率就大 通过 Tread类的 setPriority o方法设置线程的优先级,该 方法的参数为int型,其实Java提供了三个优先级别,都为 Thread类的常量,从高到低依次为 Thread.MAX- PRIORITY Thread. NORM PRIORITY、 Thread. MIN PRIORITY。这里再次 重申,优先级低并不意味着线程得不到执行,而是线程被 优先执行的概率小。这也说明设置线程的优先级不会造成 死锁的发生 Q想桃工业出隙社
9.4 线程的优先级 • 线程的有限级表示一个线程被CPU执行的机会多少。注意, 这里用“机会多少”来表达而不是用“先后顺序”来表达。 在Java中虽然定义了设置线程优先级高低的方法,但是优 先级低并不意味着在不同优先级的线程中就不会被执行, 优先级低只说明该线程被执行的概率小,同理优先级高的 线程获得CPU的概率就大。 • 通过Tread类的setPriority()方法设置线程的优先级,该 方法的参数为int型,其实Java提供了三个优先级别,都为 Thread类的常量,从高到低依次为Thread. MAX-PRIORITY、 Thread.NORM_PRIORITY、Thread.MIN_PRIORITY。这里再次 重申,优先级低并不意味着线程得不到执行,而是线程被 优先执行的概率小。这也说明设置线程的优先级不会造成 死锁的发生
编程红宝书(珍藏版) ava 完金自学手册 9.5线程的同步 在多线程中经常遇到的一个问题就是资源共享问 题,假设两个线程同时访问一个数据区,一个读 数据、一个写数据,在一个线程读数据前另一个 线程修改了数据,则读数据线程读到的不是原始 数据而是被修改过的数据,显然这样使不允许的。 而在多线程编程中经常会遇到访问共享资源的问 题,这些资源可以是数据、文件、一块内存区或 是外围设备的访问等。所以必须解决多线程编程 中如何实现资源的共享问题,在Java中称为线程 的同步问题,在多数的编程语言中解决共享资源 冲突的方法是采用顺序机制( Serialize),通过 为共享资源加锁的方法实现资源的顺序访问 Q想桃工业出隙社
9.5 线程的同步 • 在多线程中经常遇到的一个问题就是资源共享问 题,假设两个线程同时访问一个数据区,一个读 数据、一个写数据,在一个线程读数据前另一个 线程修改了数据,则读数据线程读到的不是原始 数据而是被修改过的数据,显然这样使不允许的。 而在多线程编程中经常会遇到访问共享资源的问 题,这些资源可以是数据、文件、一块内存区或 是外围设备的访问等。所以必须解决多线程编程 中如何实现资源的共享问题,在Java中称为线程 的同步问题,在多数的编程语言中解决共享资源 冲突的方法是采用顺序机制(Serialize),通过 为共享资源加锁的方法实现资源的顺序访问
编程红宝书(珍藏版) ava 完金自学手册 9.5.1Java程序的资源共享 通过下面的例子,说明如果没有实现线程同步的访问共享 资源会遇到的问题。我们设计一个线程类 FooOne,该类的 多线程代码无限循环的输出一个值,每次循环该值递增, 但递增到100时,停止循环线程退出,这由方法runQ中调 用方法 printval0实现。另一个线程类 FooTwo调用类 FooOne的对象,该类的run0方法调用类 FooOne对象的 printval0方法,也实现对变量的递增输出。我们希望是 两次调用各自完成变量的递增,相互之间不要有干扰,即 两次调用要求顺序执行。但事实上目前我们无法控制这种 顺序执行(随后会介绍 synchronized关键字解决这个问 题)。所以结果是两个线程交替执行,确实实现了并发, 或者更抽象的说两个线程同时访问了某个资源,造成数据 的不确定性。 Q想桃工业出隙社
9.5.1 Java程序的资源共享 • 通过下面的例子,说明如果没有实现线程同步的访问共享 资源会遇到的问题。我们设计一个线程类FooOne,该类的 多线程代码无限循环的输出一个值,每次循环该值递增, 但递增到100时,停止循环线程退出,这由方法run()中调 用方法printVal()实现。另一个线程类FooTwo调用类 FooOne的对象,该类的run()方法调用类FooOne对象的 printVal()方法,也实现对变量的递增输出。我们希望是 两次调用各自完成变量的递增,相互之间不要有干扰,即 两次调用要求顺序执行。但事实上目前我们无法控制这种 顺序执行(随后会介绍synchronized关键字解决这个问 题)。所以结果是两个线程交替执行,确实实现了并发, 或者更抽象的说两个线程同时访问了某个资源,造成数据 的不确定性
编程红宝书(珍藏版) ava 完金自学手册 9.5.2 synchronized关键字 在设计多线程模式中,解决线程冲突问题都是采用 synchronize关键字实现的。这意味着在给定时刻只允许 个线程访问共享资源。通常是在代码前加上一条锁语句实 现的,这就保证了在一段时间内只有一个线程运行这段代 码,如果另一个线程需要访问这段共享资源,必须等待当 前的线程释放锁。可见锁语句产生了一种互斥的效果,所 以常常称锁为“互斥量”( mutex)。 要控制对共享资源的访问,首先要把它封装进一个类,即 编写一个方法来访问共享资源,为了保证对象在调用该方 法访问资源时实现互斥访问,必须提供保证机制,保证顺 序的访问共享资源。一般来说类中的数据成员都被声明为 私有的,只有通过方法来访问这些数据。所以可以把方法 标记为 synchronized来防止资源冲突。 Q想桃工业出隙社
9.5.2 synchronized关键字 • 在设计多线程模式中,解决线程冲突问题都是采用 synchronize关键字实现的。这意味着在给定时刻只允许一 个线程访问共享资源。通常是在代码前加上一条锁语句实 现的,这就保证了在一段时间内只有一个线程运行这段代 码,如果另一个线程需要访问这段共享资源,必须等待当 前的线程释放锁。可见锁语句产生了一种互斥的效果,所 以常常称锁为“互斥量”(mutex)。 • 要控制对共享资源的访问,首先要把它封装进一个类,即 编写一个方法来访问共享资源,为了保证对象在调用该方 法访问资源时实现互斥访问,必须提供保证机制,保证顺 序的访问共享资源。一般来说类中的数据成员都被声明为 私有的,只有通过方法来访问这些数据。所以可以把方法 标记为synchronized来防止资源冲突