1、第第8章章 多线程机制多线程机制8.1 线程基础线程基础8.2 线程控制线程控制8.3 线程线程同步同步8.4 注意事项注意事项8.1 线程基础线程基础 8.1.1 进程和线程进程和线程 计算机的操作系统可以同时执行多个任务。听歌的同时能够计算机的操作系统可以同时执行多个任务。听歌的同时能够打字、下载文件,在聊天窗口打字的时候对方还能通过视频打字、下载文件,在聊天窗口打字的时候对方还能通过视频看到你。看到你。多任务是指在一个系统中可以同时运行多个程序,即有多个多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每一个任务对应一个进程。独立运行的任务,每一个任务对应一个进程。由于
2、一个由于一个CPU在同一时刻只能执行一个程序中的一条指令。在同一时刻只能执行一个程序中的一条指令。实际上,多任务运行的并发机制使这些任务交替运行,因间实际上,多任务运行的并发机制使这些任务交替运行,因间隔时间短,所以感觉就是多个程序在同时运行。隔时间短,所以感觉就是多个程序在同时运行。Java语言使用多线程来实现一个程序中的多个任务同时运行。语言使用多线程来实现一个程序中的多个任务同时运行。程序员可以在程序中执行多个线程,每个线程完成一个功能,程序员可以在程序中执行多个线程,每个线程完成一个功能,并与其他并与其他8.1 线程基础线程基础 8.1.1 进程和线程进程和线程 计算机的操作系统可以同
3、时执行多个任务。听歌的同时能够打字、下载文计算机的操作系统可以同时执行多个任务。听歌的同时能够打字、下载文件,在聊天窗口打字的时候对方还能通过视频看到你。件,在聊天窗口打字的时候对方还能通过视频看到你。多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任多任务是指在一个系统中可以同时运行多个程序,即有多个独立运行的任务,每一个任务对应一个进程。务,每一个任务对应一个进程。由于一个由于一个CPU在同一时刻只能执行一个程序中的一条指令。实际上,多任在同一时刻只能执行一个程序中的一条指令。实际上,多任务运行的并发机制使这些任务交替运行,因间隔时间短,所以感觉就是多务运行的并发机制使这些任
4、务交替运行,因间隔时间短,所以感觉就是多个程序在同时运行。个程序在同时运行。Java语言使用多线程来实现一个程序中的多个任务同时运行。程序员可以语言使用多线程来实现一个程序中的多个任务同时运行。程序员可以在程序中执行多个线程,每个线程完成一个功能,并与其他线程并发执行,在程序中执行多个线程,每个线程完成一个功能,并与其他线程并发执行,这种机制称为多线程。这种机制称为多线程。进程(进程(process)是程序的一次执行过程,是操作系统运行程序的基本是程序的一次执行过程,是操作系统运行程序的基本单位。程序是静态的,进程是动态的。系统运行一个程序就是一个进单位。程序是静态的,进程是动态的。系统运行一
5、个程序就是一个进程从创建、运行到消亡的过程。程从创建、运行到消亡的过程。系统可以为一个程序同时创建多个进程。每一个进程都有自己独立的一系统可以为一个程序同时创建多个进程。每一个进程都有自己独立的一块内存空间和一组系统资源,即使同类进程之间也不会共享系统资源。块内存空间和一组系统资源,即使同类进程之间也不会共享系统资源。操作系统进程1进程2进程3线程1线程2线程3线程具有生命周期,它包含线程具有生命周期,它包含3个状态:出生状态、就绪状态、运行状态。个状态:出生状态、就绪状态、运行状态。出生状态:用户在创建线程时处于的状态,在用户使用该线程实例调用出生状态:用户在创建线程时处于的状态,在用户使用
6、该线程实例调用start()方法之前都处于出生状态。方法之前都处于出生状态。就绪状态:用户调用就绪状态:用户调用start()之后,线程就处于就绪状态了。之后,线程就处于就绪状态了。运行状态:当线程得到系统资源后就进入运行状态。运行状态:当线程得到系统资源后就进入运行状态。8.1.2线程的生命周期线程的生命周期出生就绪执行等待休眠死亡阻塞Thread t=new Thread();t.start()t.notify()或t.notifyAll()时间片结束得到系统资源t.wait()t.sleep()t.run()例例8.1 获取当前线程对象,并输出当前线程的名称。获取当前线程对象,并输出当前
7、线程的名称。public class MainThread public static void main(String args)System.out.println(main主方法开始运行主方法开始运行);Thread currentThread=Thread.currentThread();System.out.println(成功获取当前线程对象成功获取当前线程对象);String name=currentThread.getName();System.out.println(当前线程的名称是:当前线程的名称是:+name);8.1.3 认识认识main线程线程Thread类是类是Ja
8、va语言的线程类,位于语言的线程类,位于java.lang包中。包中。该类的实例对象是线程对象,所以继承该类编写线程子类该类的实例对象是线程对象,所以继承该类编写线程子类是实现多线程的方法之一。是实现多线程的方法之一。通过通过Thread类创建线程子类的格式为:类创建线程子类的格式为:class 线程的类名线程的类名 extends Thread public void run()程序语句程序语句 Thread类的类的start()方法用于启动一个线程对象。方法用于启动一个线程对象。public void start()执行该方法后,执行该方法后,JVM启动该线程对象,并调用该线程的启动该线程
9、对象,并调用该线程的run()方法。方法。8.1.4 通过继承通过继承Thread类创建线程类创建线程 例例8.2 在项目中创建在项目中创建Writer作者类,该类继承作者类,该类继承Thread类并重写类并重写run()方法称为线程类,在主方法方法称为线程类,在主方法中创建两个作者类的实例对象,分别设置中创建两个作者类的实例对象,分别设置name属属性为性为“张三张三”和和“李四李四”,然后同时启动两个线程,然后同时启动两个线程,在控制台查看两个线程的运行情况。在控制台查看两个线程的运行情况。8.1.4 通过继承通过继承Thread类创建线程类创建线程 public class Writer
10、 extends Thread private String name=未知未知“;/声明作者名声明作者名public Writer(String name)this.name=name;/初始化作者名称初始化作者名称public void run()/重写重写run()方法方法while(true)System.out.println(name+:写一段文稿写一段文稿);/输出工作进度输出工作进度 System.out.println(name+:吸一口烟吸一口烟,放松一下。放松一下。);/输出工作状态输出工作状态 try Thread.sleep(int)(Math.random()*10
11、000);/随机休息几秒随机休息几秒catch(InterruptedException e)e.printStackTrace();public static void main(String args)Thread writer1=new Writer(“张三张三);/创建作者创建作者1线程线程Thread writer2=new Writer(“t李四李四);/创建作者创建作者2线程线程writer1.start();/张三线程开始工作张三线程开始工作writer2.start();/李四线程同时启动李四线程同时启动图图 8.2 运行结果运行结果(1)Application应用程序运行时
12、总是调用应用程序运行时总是调用main方方法,因此法,因此main是创建和启动线程对象的地方。是创建和启动线程对象的地方。main本身也是一个线程,是程序自动拥有的一个本身也是一个线程,是程序自动拥有的一个线程,称为主线程。在线程,称为主线程。在main方法中创建了两个方法中创建了两个testThread线程对象:线程对象:writer1与与writer2,并在创并在创建后马上调用建后马上调用start方法启动了这两个线程。方法启动了这两个线程。(2)从输出的结果可以看出两个线程的名字是交从输出的结果可以看出两个线程的名字是交替显示的,这是因为两个线程是同步的,于是,两替显示的,这是因为两个线
13、程是同步的,于是,两个个run方法也同时被执行。线程语句的顺序只是决方法也同时被执行。线程语句的顺序只是决定于线程执行的顺序,线程的执行顺序是由系统调定于线程执行的顺序,线程的执行顺序是由系统调度和控制的。度和控制的。(3)由继承由继承Thread创建的子类,必须覆盖创建的子类,必须覆盖run方方法,因为法,因为Thread线程类的线程类的run方法是空的。方法是空的。run是是线程类的关键方法,线程的所有活动都是通过它来线程类的关键方法,线程的所有活动都是通过它来实现的。当调用线程对象时系统就自动调用其实现的。当调用线程对象时系统就自动调用其run方法,正是通过方法,正是通过run方法才使创
14、建线程的目的得以方法才使创建线程的目的得以实现。实现。run方法的作用如同方法的作用如同Application应用程序的应用程序的main方法一样。方法一样。由例子可以看出,创建线程对象,就是设计由例子可以看出,创建线程对象,就是设计run方方法。一旦启动线程对象,就进入法。一旦启动线程对象,就进入run方法,便执行方法,便执行run中的所有语句,中的所有语句,run方法执行完毕,这个线程也方法执行完毕,这个线程也就结束了。就结束了。当一个类是从其他类继承时,如继承当一个类是从其他类继承时,如继承Frame类。此时就不能类。此时就不能再继承再继承Thread类来创建线程。这时可以通过接口类来创
15、建线程。这时可以通过接口Runnable直接创建线程对象。接口直接创建线程对象。接口Runnable是一个抽象接口,接口是一个抽象接口,接口中只声明了一个未实现的中只声明了一个未实现的run方法。方法。package java.lang;public interface Runnablepublic abstract void run();实现实现Runnable接口的对象需要传递给接口的对象需要传递给Thread类的构造方法,通过类的构造方法,通过Thread构造方法去创建线程类,也就是说构造方法去创建线程类,也就是说Runnable接口的实现对象需接口的实现对象需要传递给要传递给Threa
16、d的实例对象才能启动线程的实例对象才能启动线程。Thread(Runnable target)Thread(Runnable target,String name)8.1.5 通过通过Runnable接口创建线程接口创建线程 使用使用Runnable接口启动新线程的步骤如下:接口启动新线程的步骤如下:(1)建立)建立Runnable接口实例对象。接口实例对象。(2)以)以Runnable接口实例对象创建接口实例对象创建Thread实例对象。实例对象。(3)调用)调用Thread线程对象的线程对象的start()方法启动线程。()方法启动线程。Thread对象Runnable对象启动线程,执行r
17、un()方法中的代码根据Runnable对象创建Thread对象调用start()方法例8.2 创建窗体,在窗体中布局一个文本域组件。见案例RunnableDemo.java两种创建线程方法的比较:两种创建线程方法的比较:(1)由继承由继承Thread类创建线程对象简单方便,可类创建线程对象简单方便,可以直接操作线程,但不能再继承其他类;以直接操作线程,但不能再继承其他类;(2)在继承其他类的类中可用在继承其他类的类中可用Runnable接口创建接口创建线程对象。可保持程序风格的一致性。线程对象。可保持程序风格的一致性。1、线程的休眠:、线程的休眠:tryThread.sleep(2000);
18、catch(InterruptedException e)e.printStackTrace();8.2 线程控制线程控制2、线程的停止:、线程的停止:早期早期JDK版本中使用版本中使用stop()方法停止线程,新版本方法停止线程,新版本中废除了。现在提倡在中废除了。现在提倡在run()方法中使用布尔型标方法中使用布尔型标记控制循环的停止。记控制循环的停止。例如:例如:public class InterruptedTest implements Runnableprivate boolean isContinue=false;public void run()while(true)if(is
19、Continue)break;public void setContinue()this.isContinue=true;3、线程的调度线程的调度当前某程序为多线程程序,假如存在一个线程当前某程序为多线程程序,假如存在一个线程A,此时需要插入线程此时需要插入线程B,并要求线程,并要求线程B先执行完毕,先执行完毕,然后在继续执行线程然后在继续执行线程A,这时可以使用,这时可以使用join()方法。方法。就好比你正在看电视却突然有人上门收水费,你必就好比你正在看电视却突然有人上门收水费,你必须付完水费才能继续看电视。见案例须付完水费才能继续看电视。见案例JoinTest.java在单线程程序中,每
20、次只能做一件事情,后面的事情也需在单线程程序中,每次只能做一件事情,后面的事情也需要等待前面的事情完成后才可以进行,如果使用多线程程要等待前面的事情完成后才可以进行,如果使用多线程程序,虽然能够实现多处理,但是会发生两个以上的线程抢序,虽然能够实现多处理,但是会发生两个以上的线程抢占资源的问题,例如两个人同时使用一个麦克风,多个人占资源的问题,例如两个人同时使用一个麦克风,多个人同时向同一个账户汇款等。在多线程编程中,必须防止这同时向同一个账户汇款等。在多线程编程中,必须防止这些资源访问的冲突。些资源访问的冲突。Java提供线程同步的机制可以防止资源访问的冲突。提供线程同步的机制可以防止资源访
21、问的冲突。8.3 线程的同步机制线程的同步机制8.3.1 线程安全线程安全火车站售票系统火车站售票系统-在代码中判断网络中当前剩在代码中判断网络中当前剩余票数是否大于余票数是否大于0,如果大于,如果大于0则执行售票功能则执行售票功能-当两个线程同时访问这段代码时(假如这时当两个线程同时访问这段代码时(假如这时只剩下只剩下1张票),第一个线程将票售出张票),第一个线程将票售出-第二个线程已经执行完成判断是否有票的操作,并第二个线程已经执行完成判断是否有票的操作,并得出结论票数得出结论票数0,这样它售出票那么就会产生负,这样它售出票那么就会产生负数。见案例数。见案例ThreadSaftTest.j
22、ava 8.3.2 线程同步机制线程同步机制如何解决资源共享的问题,基本上所有解决多线程资源冲突问题的方法,都是在指如何解决资源共享的问题,基本上所有解决多线程资源冲突问题的方法,都是在指定时间段内只允许一个线程访问共享资源,这时就需要给共享资源上一道锁了。定时间段内只允许一个线程访问共享资源,这时就需要给共享资源上一道锁了。1。同步块。同步块synchronized(object)/业务代码业务代码object:同步块锁定的对象:同步块锁定的对象通常将共享资源的操作放置在通常将共享资源的操作放置在synchronized定义的代码内,这样其它线程也获取到定义的代码内,这样其它线程也获取到这个
23、锁时,必须等待代码块的对象锁被释放时才能进入该区域。这个锁时,必须等待代码块的对象锁被释放时才能进入该区域。案例案例ThreadSafeTest.java是对是对ThreadSaftTest.java的修改。的修改。2。同步方法。同步方法synchronized void syncMethod(。)。/方法体方法体档某个对象调用了同步方法,其它方法必须等待该同步方法执行完毕才能被执行。档某个对象调用了同步方法,其它方法必须等待该同步方法执行完毕才能被执行。所以将每个能访问共享资源的方法修饰为所以将每个能访问共享资源的方法修饰为synchronized可以防止多个县城同时修改可以防止多个县城同时
24、修改或访问共享资源。见案例或访问共享资源。见案例ThreadSafe.java考虑到多线程时,人们立刻会想到某些任务是可以考虑到多线程时,人们立刻会想到某些任务是可以使用多线程的,例如数据计算、数据库查询,以及使用多线程的,例如数据计算、数据库查询,以及输入的获得。因为这些任务通常都被认为是后台任输入的获得。因为这些任务通常都被认为是后台任务,不直接与用户打交道。在务,不直接与用户打交道。在Java语言程序设计中,语言程序设计中,动态效果的程序都会使用多线程,例如动画的播放、动态效果的程序都会使用多线程,例如动画的播放、动态的字幕,等等。动态的字幕,等等。8.4 何时使用多线程及注意问题何时使
25、用多线程及注意问题 在程序中使用多线程是有代价的。它会对系统产生在程序中使用多线程是有代价的。它会对系统产生以下影响:以下影响:(1)线程需要占用内存;线程需要占用内存;(2)线程过多,会消耗大量线程过多,会消耗大量CPU时间来跟踪线程;时间来跟踪线程;(3)必须考虑多线程同时访问共享资源的问题,必须考虑多线程同时访问共享资源的问题,如果没有协调好,就会产生令人意想不到的问题,如果没有协调好,就会产生令人意想不到的问题,例如可怕的死锁和资源竞争;例如可怕的死锁和资源竞争;(4)因为同一个任务的所有线程都共享相同的地因为同一个任务的所有线程都共享相同的地址空间,并共享任务的全局变量,所以程序也必须址空间,并共享任务的全局变量,所以程序也必须考虑多线程同时访问全局变量的问题。考虑多线程同时访问全局变量的问题。
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。