ImageVerifierCode 换一换
格式:DOCX , 页数:32 ,大小:276.76KB ,
资源ID:3733157      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/3733157.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Java多线程讲义.docx)为本站会员(b****4)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Java多线程讲义.docx

1、Java多线程讲义Java多线程编程1.线程的概念首先我们从以下几个方面来了解线程的概念。进程和线程的区别线程的概念模型线程的运行状态线程的优先级1.1 进程和线程的区别1.1.1 多个进程在大多数操作系统中都可以创建多个进程。当一个程序启动时,它可以为即将开始的每个任务创建一个进程,并允许他们同时运行。当一个程序因等待网络访问或用户输入而阻塞时,另一个程序还可以运行,这样就增加了资源利用率。但是,按照这种方式创建每一个进程都要付出一定代价:设置一个进程要占用相当一部分处理器时间和内存资源。而且,大多数操作系统不允许进程访问其他的内存空间。因此,进程间的通信非常不方便。并且也不会将它自己提供给

2、简单的编程模型。如图10-1所示,Word、Excel、IE等每一个软件都可以独立启动,它们都代表一个独立的进程,而它们各自都有自己的内存空间,这些内存空间之间相互独立互不共享,各自也能够并行运行。1.1.2 多个线程线程也为轻型进程(LWP)。因为线程只能在单个进程的作用域内活动,所以创建线程比创建进程更廉价的多。这样,因为线程允许协作和数据交换,并且在计算资源方面非常廉价,所以线程比进程更可取。线程需要操作系统的支持,因此不是所有的机器都提供线程。java编程语言,作为一种相当新的语言,已将线程和语言本身合为一体,这样就对线程提供了强健的支持。如图10-2所示,线程1,线程2到线程n,它们

3、都代表一个独立的线程,运行在一个进程中,因此它们拥有同一块内存区域,可以共享内存中的数据。它们可以并行执行,但实际上在CPU的调用上是各自分割时间片的,并且是顺序执行的,如图中的粗线条所示。1.2 线程的概念模型线程是彼此相互独立的、能独立运行的子任务,并且每一个线程都有自己的调用栈。所谓的多任务是通过周期性地将CPU时间片切换到不同的子任务,虽然在微观上看来,单合CPU只能运行一个子任务,但从宏观上来看,每个子任务似乎是同时连续运行的。我们现在使用的大多数操作系统都属于多任务、分时操行系统。正是由于这种操作系统的出现才有了多线程这个概念,我们使用的windows、linux都属于此列。1.2

4、.1.1 分时什么是分时操作系统呢?通俗点讲,就是可以同一时间执行多个程序的操作系统,在自己的电脑上面,你是不是一边听歌,一边聊天还可以一边看网页?实际上,并不是CPU在同时执行这些程序,CPU只是将时间切割为时间片,然后将时间片分配给这些程序,获得时间片的程序开始执行,不等执行完毕,下个程序又获得时间片开始执行,这样多个程序轮流执行一段时间,由于现在CPU的高速计算能力,给人的感觉就好像是多个程序在同时执行一样。一般可以在同一时间内执行多个程序的操作系统都有进程的概念。一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间、一组系统资源。在进程概率中,每一个进程内部数据和状态都

5、是完全独立的。因此可见创建一个进程并执行他的开销是很大的,所以线程出现了。在Java中,程序通过流控制来执行程序流,程序中单个顺序的流控制称为线程,多线程则指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务。多线性意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行。线程与进程相似,是完成某个特定功能的一段代码,是程序中单个顺序的流控制;但与进程不同的是,同类的多个线程是共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈。所以系统在产生一个线程,或者在各个线程之间切换时,负担要比进程小的多,正因如此,线程也被称为轻负荷进

6、程(light-weight process)。一个进程中可以包含多个线程。1.2.1.2 多任务多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每个任务对应一个进程,同进程一样,一个线程也有从创建、运行到消亡的过程,称为线程的生命周期。用线程的状态(state)表明线程处在生命周期的那个阶段。线程有创建、可运行、运行中、阻塞、死亡5中状态。通过线程的控制与调度可使线程在这个几种状态间转化每个程序至少自动拥有一个线程,称为主线程。当程序加载到内存时,启动主线程。1.3 线程的运行状态 在java中,多线程就是一个类或一个程序执行或管理多个线程执行任务的能力,每一个线程可以独

7、立于其他线程而独立运行,当然也可以和其他线程协同运行,一个类控制着它的所有线程,可以决定那个线程得到优先级,哪个线程可以访问其他类的资源,哪个线程开始执行,哪个保持休眠状态。根据线程的调度过程,线程可以处以不同的执行状态,如图10-3所示,是线程的运行机制图。线程的状态表示线程正在进行的活动及在此时间段内所能完成的任务。线程有创建、可运行、运行中、阻塞、死亡5种状态。一个具有生命的线程,总是处于这5种状态之一。新建状态:使用new运算符创建一个线程后,该线程仅仅是一个空对象,系统没有分配资源,称该线程为创建状态(new Thread)可运行状态:使用start()方法启动一个线程后,系统为该线

8、程分配了除CPU外的所需资源,使该线程处于可运行状态(Runnable)。运行状态:Java运行系统通过调度选中一个Runnable的线程,使其占用CPU并转为运行中状态(Running),此时系统真正执行线程的run()方法。阻塞状态:一个正在运行的线程因某种原因不能继续运行时,进入阻塞状态(Blocked)。死亡状态:线程结束后事死亡状态(Dead).1.4 线程的优先级从概念上来说,线程是并发运行的,但是从计算机的运行角度来说,却是串行执行的。当系统中只有一个CPU时,线程会以某一种顺序执行,这称作线程调度(scheduling).Java采用的是一种简单、固定的调度法,即固定优先级调度

9、。这种算法是根据处于可运行态线程的相对优先级来实行调度。当线程产生时,它继承原线程的优先级。在需要时可对优先级进行修改。在任何时刻,如果有多条线程等待运行,系统选择优先级最高的可运行线程运行。只有当它停止、自动放弃或由于某种原因成为非运行态低优先级的线程才能运行。如果两个程序拥有同样的优先级,它们将被交替地运行。Java实时系统的线程调度算法还是强制性的,在任何时刻,如果一个比其他线程优先级都高的线程的状态变为可运行态,实时系统将选择该线程来运行。同一时刻如果有多个线程处于可运行状态,则它们需要排队等待CPU资源。此时每个线程自动获得一个线程的优先级(priority),优先级的高低反映线程的

10、重要或紧急程度。可运行状态的线程按优先级排队,线程调度优先级基础上的“先到先服务”原则。线程调度管理器负责线程排队和CPU在线程间的分配,并由线程调度算法进行调度。当线程调度管理器选中某个线程时,该线程获得CPU资源而进入运行状态,则这个线程立即被调度执行。先式调度分为:独占方式和分时方式。独占方式:当前执行线程将一直执行下去,直到执行完毕或由于某种原因主动放弃CPU或者CPU被一个更高优先级的线程占领。分时方式:当前运行线程获得一个时间片,当时间到时,即使没有执行完也要让出CPU进入可运行状态,等待下一个时间片的调度,系统选中其他可运行状态的线程执行。分时方式的系统使每个线程工作若干步,实现

11、多线程同时运行。2. 线程的开发方法线程所有的操作都发生在线程体中,在Java中线程体是从Thread类继承的run()方法,或实现Runnable接口的类中的run()方法。当线程产生并初始化后,实时系统调用它的run()方法。run()方法内的代码实现所产生线程的行为,它是线程的主要部分。本小节将首先讲解Java程序中进行多进程调度的两种方法。使用Runtime类使用ProcessBuilder类然后再讲解在Java中实现一个线程的两种方法。第一种是实现Runnable接口实现它的run()方法。第二种是继承Thread类,覆盖它的run()方法。这两种方法的区别是,如果你的类已经继承了其

12、他的类,那么你只能选择实现Runnabler接口了,因为Java只允许单继承。2.1 使用进程调用Java程序一般我们在Java中运行其他类中的方法时,无论是静态调用,还是动态调用,都是在当前的进程中执行的,也就是说,只有一个Java虚拟机实例在运行。而有的时候,我们需要通过Java代码去启动多个Java子进程。这样虽然会占用一些系统资源,但会使程序更加稳定,因为新启动的程序是在不同的虚拟机进程中运行的,如果有一个进程发生异常,并不影响其他子进程。在Java中,我们可以使用两种方法来实现这种要求。最简单的方法就是通过Runtime中的exec()方法执行Java类名。如果执行成功,则这个方法返

13、回一个Process对象,如果执行失败,那么将抛出一个IOException错误。下面让我们来看一个简单的例子。(1)编写子进程程序我们先编写一个Java程序,用来创建一个文件,如程序10-1所示。public class FileTest public static void main(String args) throws Exception File file = new File(D:/work/demo/newFile.txt); file.createNewFile(); System.out.println(被调用成功了!); 通过“java FileTest”运行程序后,发现在

14、D:/demo下多了个newFile.txt文件,在控制台中出现“被调用成功!”的输出信息(2)使用Runtime调用子进程程序下面是通过Runtime调用上面的Java程序的方式,如程序10-2所示。public class RuntimeTest public static void main(String args) throws IOException Runtime runtime = Runtime.getRuntime(); runtime.exec(java FileTest); 通过“Runtime”运行程序后,发现在D:/demo下多个newFile.txt文件,但在控制台

15、中并未出现“子进程输出:被调用成功!”的输出信息。因此可以判定,FileTest已经被执行成功,但因为某种原因,FileTest的输出信息未在RuntimeTest的控制台中输出。这个原因也很简单,因为使用exec()建立的是RuntimeTest的子进程,这个子进程并没有自己控制台,因此,它并不会输出任何信息。(3)获取子进程程序的输出信息如果想获取子进程的输出信息,可以通过Process中的getInputStream得到子进程的输出流(在子进程中输出,在父进程中就是输入),然后将子进程中的输出流从父进程的控制台输出。具体的实现代码如程序10-3所示。public class Runtim

16、eTest public static void main(String args) throws IOException Runtime runtime = Runtime.getRuntime(); Process p=runtime.exec(java FileTest); BufferedInputStream bis = new BufferedInputStream(p.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(bis); String s; while(s=br.r

17、eadLine()!=null) System.out.println(控制台输出如下:+s); bis.close(); br.close(); 从上面的代码可以看出,在RuntimeTest.java中通过按行读取子进程的输出信息,然后再RuntimeTest中按行进行输出。(4)给子进程传递输入信息上面讨论的是如何得到子进程的输出信息。那么,除了输出信息,还有输入信息。既然子进程没有自己的控制台,那么输入信息也得由父进程提供。我们可以通过Process的getOutStream()方法来为子进程提供输入信息(即由父进程向子进程输入信息,而不是由控制台输入信息),我们可以看看如下的代码。为

18、TestFile.java添加读入外部输入信息的代码:BufferedReader br = new BufferedReader(new InputStreamReader(System.in);System.out.println(由父进程输入的信息:+br.readLine();添加向子进程输入的代码,如程序10-4所示。public class RuntimeTest public static void main(String args) throws IOException Runtime runtime = Runtime.getRuntime(); Process p=runt

19、ime.exec(java FileTest);BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(p.getOutputStream(); bw.write(Hello world!); bw.flush(); bw.close(); BufferedInputStream bis = new BufferedInputStream(p.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(bis); String s;

20、while(s=br.readLine()!=null) System.out.println(控制台输出如下:+s); bis.close(); br.close(); 从以上代码可以看出,Test1得到由Test_Exec_In发过来的信息,并将其输出。当你不加bw.flush()和bw.close()时,信息将无法到达子进程,也就是说子进程进入阻塞状态,但由于父进程已经退出了,因此,子进程也跟着退出。如果要证明这一点,可以在最后加上System.in.read(),然后通过任务管理器(在Windows下)查看Java进程,你会发现如果加上bw.flush()和bw.close(),只有一

21、个Java进程存在,如果去掉它们,就有两个Java进程存在。这是因为,如果将信息传给Test2,在得到信息后,Test2就退出了。在这里有一点需要说明一下,exec的执行是异步的,并不会因为执行的某个程序阻塞而停止执行下面的代码。因此,可以在运行Test2后,仍可以执行下面的代码。exec()方法经过多次的重载,上面使用的只是它的一种重载,它还可以将命令和参数分开,如exec(java.test2)可以写成exec(java,test2).exec还可以通过指定的环境变量运行不同配置的Java虚拟机。(5)使用ProcessBuilder建立子进程除了使用Runtime的exec()方法建立子

22、进程外,还可以通过ProcessBuilder建立子进程,ProcessBuilder的使用方法如程序10-5的粗体部分所示。public class ProcessBuilderTest public static void main(String args) throws IOException ProcessBuilder pb = new ProcessBuilder(java,FileTest); Process process= pb.start(); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(pro

23、cess.getOutputStream(); bw.write(Hello Wolrd!); bw.flush(); bw.close(); BufferedInputStream bis = new BufferedInputStream(process.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(bis); String s; while(s=br.readLine()!=null) System.out.println(子进程输出如下:+s); bis.close(); b

24、r.close(); 在建立子进程上,ProcessBuilder和Runtime类似,不同的ProcessBuilder使用start()方法启动子进程,而Runtime使用exec()方法启动子进程。得到Process后,它们的操作就完全一样了。ProcessBuilder和Runtime一样,也可设置可执行文件的环境信息、工作目录等。2.2 第一种方法-继承ThreadThread类是一个具体的类,即不是抽象类,该类封装了线程的行为。public class Threadextends Objectimplements Runnable线程是程序中的执行线程。Java虚拟机允许应用程序并

25、发地运行多个执行线程。每个线程都有一个优先级,高优先级线程执行优先于低优先级线程。每个线程可以标记为一个守护程序。当某个线程中运行的代码创建一个新Thread对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当只创建线程是守护线程时,新线程才是守护程序。当Java虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的main方法)。Java虚拟机会继续执行线程,指定下一列任意情况出现时为止。调用了Runtime类的exit()方法,并且安全管理器允许退出操作发生。非守护线程的所有线程都已停止运行,无论是通过从对run()方法的调用中返回,还是通过抛出一个传播到run()方法之

26、外的异常。创建新执行线程有两种方法。一种方法是将类声明为Thread的子类。该子类应重写Thread类的run()方法。接下来可以分配并启动该子类的实例。例如,计算大于莫一规定值的质数的线程可以写出:public class PrimeThreadTest extends Thread long minPrime; public PrimeThreadTest(long minPrime) this.minPrime = minPrime; public void run() /. 当使用继承创建线程时,可以这样启动线程:new MyThread_1().start();下例代码会创建并启动一

27、个线程:PrimeThread p = new PrimeThread(143);p.start();程序10-6演示了创建两个线程对象的实例。public class ThreadTest extends Thread public int time; public String user; public ThreadTest(int time,String user) this.time=time; this.user=user; public void run() while(true) try System.out.println(user+休息+time+ms-+new Date()

28、; Thread.sleep(time); catch (InterruptedException e) System.out.println(e); public static void main(String args) ThreadTest t1 = new ThreadTest(1000,T1); t1.start(); ThreadTest t2 = new ThreadTest(3000,T2); t2.start(); 在本例中,我们可以看到一个简单的程序,它按两个不同的时间间隔(1秒和3秒)在屏幕上显示当前时间。这个事通过创建两个新线程来完成的,包括main()共3个进程。这里

29、我们共创建了2个线程实例,分别间隔1秒和3秒输出一次字符串信息。运行后控制台的输出如下:从运行的结果来看,两个线程是并行运行的,先输出的是t1的内容。如果要控制线程的执行顺序,那么可以为线程设置优先级:t1.setPriority(1);t2.setPriority(Thread.MAX_PRIORITY);这是优先执行t2,输出的结果如下:2.3 第二种方法-实现Runnable上面我们使用Thread方法创建一个线程类,但是有时候要作为线程运行的类已经是某一个类的子类,Java中只允许单一继承,所有不能按照这种机制创建线程。而Java同时为我们提供了解决的方法,即是在同一个类中实现任意数量

30、的接口-Runnable接口。public interface RunnableRunnable接口应该由哪些打算通过莫一线程执行其实例。类必须定义一个名为run()的无参方法。void run();在使用实现接口Runnable的对象创建一个线程时,启动该行程导致在独立执行的线程中调用对象的run()方法。此外Runnable为非Thread子类的类提供了一种激活方式。通过实例化某个Thread实例并将自身作为运行目标,就可以运行实例Runnable的类而无须创建Thread的子类。在大多数情况下,如果只想重写run()方法,而不重写其他Thread方法,而不重写其他Thread方法,那么应

31、使用Runnable接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。因此,创建线程的例子如下所示:class PrimeRun implements Runnable long minPrime; PrimeRun(long minPrime) this.minPrime = minPrime; public void run() /compute primes larger than minPrime 当使用实现接口创建线程时,可以这样启动线程:new Thread(new MyTread_2().start();下列代码创建并启动一个线程;PrimeRun p = new PrimeRun(14

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1