收藏 分享(赏)

第14章 多线程.ppt

上传人:hskm5268 文档编号:10035902 上传时间:2019-09-30 格式:PPT 页数:58 大小:457KB
下载 相关 举报
第14章  多线程.ppt_第1页
第1页 / 共58页
第14章  多线程.ppt_第2页
第2页 / 共58页
第14章  多线程.ppt_第3页
第3页 / 共58页
第14章  多线程.ppt_第4页
第4页 / 共58页
第14章  多线程.ppt_第5页
第5页 / 共58页
点击查看更多>>
资源描述

1、第14章 多线程编程,第14章 多线程,多线程基本概念创建线程的方式线程的生命周期及控制线程的优先级及调度多线程的互斥与同步,程序的一次执行称为进程(process)。一个进程包括一个程序模块和该模块一次执行时所处理的数据线程(thread)是指进程内部一段可独立执行的有独立控制流的指令序列。,14.1多线程基本概念,多线程与多进程的主要区别: 一个进程可以拥有若干个线程; 在多进程设计中各个进程之间的数据块是相互独立的,一般彼此不影响,要通过信号等进行交流; 多线程设计中,各个线程不一定独立,同一任务中的各个线程共享程序段、数据段等资源。,14.1、多线程基本概念,程序:静态的概念 进程:程

2、序的一次执行过程,每个进程都有独立的代码和数据空间,进程切换的开销大。 线程:轻量的进程,同一类线程共享代码和数据空间,线程切换的开销小。 多任务:一个操作系统可以同时执行多个程序,即多个独立运行的程序,每个程序针对一个任务 多进程:在操作系统中,能同时运行多个任务程序; 多线程:在同一应用程序中,有多个顺序流同时执行。,14.1、多线程基本概念,14.1、多线程基本概念,多线程编程简单,效率高 能直接共享数据和资源,多进程不能适合于开发服务程序 如,Web服务,聊天服务等适合于开发有多种交互接口的程序如聊天程序的客户端,网络下载工具 减轻编写交互频繁、涉及面多的程序的困难;程序的吞吐量会得到

3、改善; 有多个处理器的系统,可以并发运行不同的线程,14.1.2 多线程的优势,用C、C+或其他语言对数据同步的支持不充分;Java语言提供了Thread类来实现多线程。在Java中,线程可以认为是由三部分组成的:虚拟CPU 封装在java.lang.Thread类中,它控制着整个线程的运行执行的代码 传递给Thread类,由Thread类控制顺序执行;处理的数据 传递给Thread类,是在代码执行过程中所要处理的数据。,14.1.3、java支持多线程,Java的线程是通过Java的软件包java.lang中定义的类Thread来实现的。 1.Thread类本身只是线程的虚拟CPU; 2.线

4、程所执行的代码(或者说线程所要完成的功能)是通过方法run( ) 来完成的,方法run( )称为线程体; 3.在一个线程被建立并初始化以后,Java的运行时系统就自动调用run( )方法,正是通过run( )方法才使得建立线程的目的得以实现。,14.2 创建线程,用户可以有两种方法构造自己的run( )方法; 方法一:继承Thread类 定义一个类,继承Thread的线程类,重写其中的run();方法二: Runnable接口定义一个类,实现Runnable的接口。重写其中的run();该类对象作为Thread类的参数。,14.2.2 线程的构造,从Thread类的构造方法可以看出,用户可以有

5、两种方法构造自己的run( )方法; 方法一:Thread类 定义一个继承Thread的线程类,并重写其中的方法run();例14.1 继承Thread类创建线程,14.2.2 线程的构造,public class Thread1 extends Thread int k=0;public Thread1(String name,int k) super(name); this.k = k; public void run() /覆盖run方法的线程体, int i = k; System.out.println();System.out.print(getName()+“: “);while

6、 (i100) System.out.print(i+“ “); i+=2; System.out.println(getName() +“ end!“);,public static void main (String args) Thread1 t1 = new Thread1(“Thread1“,1); /创建对象Thread1 t2 = new Thread1(“Thread2“,2);t1.start(); t2.start(); /启动执行线程System.out.println(“activeCount=“+activeCount(); ,程序说明,两个线程交替运行,从宏观看是同

7、时运行 线程运行的特点:运行结果的不确定性 Main本身也是一个线程 本例的main方法产生并启动了来两个线程t1、t2。所以输出的活动线程个数是三个: activeCount=3 3. 程序中线程语句顺序只决定其产生的顺序,线程产生后并不立即运行,而是与系统中的其他线程一起等待系统的执行; 所有线程执行的机会是均等的; 线程的执行顺序是由系统的调度决定的;,程序说明,方法二: Runnable接口提供一个实现接口Runnable的类作为线程的目标对象。在初始化一个Thread类或子类生成线程实例时,把目标对象传递给这个线程实例,由该目标对象提供线程体run()方法; 这时,实现接口Runna

8、ble的类还可以再继承其他类,即实现接口Runnable的类可以不单纯是提供线程体; 例14.2 实现Runnable接口创建线程,14.2.2 线程的构造,public class Runnable1 implements Runnable int k=0;public Runnable1(int k) this.k = k; public void run() int i = k;System.out.println();while (i50) System.out.print(i+“ “); i+=2; ,Clock.java,public static void main (Strin

9、g args) Runnable1 r1 = new Runnable1(1); /创建线程体的目标对象Runnable1 r2 = new Runnable1(2);Thread t1=new Thread(r1); /以目标对象创建线程Thread t2=new Thread(r2);t1.start();t2.start(); ,Clock.java,run ()方法是运行线程的主体,启动线程时,自动调用run( )Runnable接口 可以将线程的虚拟CPU、代码和数据分开,形成一个比较清晰的模型; 使得包含线程体的类还可以继承其他类;直接继承Thread类 不能再继承其他类,但编写简

10、单,并可直接操纵线程; 在具体应用中,采用哪种方法来构造线程体要根据具体情况而定; 通常,当一个线程体所在的类已经继承了另一个类时,就应该用实现Runnable接口的方法。,两种创建线程的比较,void run() 线程所执行的代码 void start() throws IllegalThreadStateException 使程序开始执行,多次调用会产生例外 void sleep(long milis) 让线程睡眠一段时间,此期间线程不消耗CPU资源 void interrupt() 中断线程,设置一个中断标记 boolean isInterrupted() 判断指定线程是否被中断,Thr

11、ead类的方法,boolean isAlive() 判断线程是否处于活动状态(即已调用start,但run还未返回) static Thread currentThread() 返回当前线程对象的引用 void setName(String threadName) 改变线程的名字 String getName() 获得线程的名字 void join(long millis, int nanos) 等待线程结束,Thread类的方法,Thread类的方法 void destroy() 销毁线程 void stop() 终止线程的执行 void suspend() / void resume()

12、挂起线程 / static void yield() 暂停当前线程,让其他线程执行 notify() / notifyAll() / wait() 后面介绍,Thread类的方法,14.3 线程的生命周期及控制,线程是程序内部的一个顺序控制流,它具有一个 特定的生命周期; 在一个线程的生命周期中,它总处于某一种状态中 线程的状态表示了线程正在进行的活动以及在这段 时间内线程能完成的任务。,runing,Suspend() sleep() wait() I/O流阻塞,resume() notify()/notifyAll() I/O指令,waiting sleeping suspending b

13、locked,ready running,14.3 线程的生命周期及控制控制,在Java中线程的优先级是用数字来表示的,分为三个级别:Thread.MIN_PRIORITY 低优先级,默认值为1 (24) Thread. NORM_PRIORITY 缺省优先级,数值为5 Thread.MAX_PRIORITY 高优先级,默认值为10 (69)可以用方法 int getPriority()来获得线程的优先级,同时也可以用方法 void setPriority( int p ) 在线程被创建后改变线程的优先级。,14.4 线程的优先级及调度,例14.3 线程的优先级设置,14.4 线程的优先级及调

14、度,class MyThread extends Thread String message;MyThread ( String message ) this.message = message;public void run() for ( int i=0; i3; i+ )System.out.println( message+“ “+getPriority() ); /获得优先级,public class ThreadTestpublic static void main( String args ) Thread t1 = new MyThread(“T1“);t1.setPriori

15、ty( Thread.MIN_PRIORITY ); /设置优先级为最小t1.start( ); Thread t2 = new MyThread(“T2“);t2.setPriority( Thread.MAX_PRIORITY ); /设置优先级为最大t2.start( ); Thread t3 = new MyThread(“T3“);t3.setPriority( Thread.MAX_PRIORITY ); /设置优先级为最大t3.start( ); ,同时运行的线程若果需要共享数据,就要求保证共享数据的一致性。,class Stackint index = 0;char data

16、= new char6;public void push(char c)dataindex = c;index+;public char pop()index-;return dataindex; ,14.5 多线程的同步机制,先进栈,index值再增加,当有两个线程A和B同时使用了Stack类的一个实例时,在某一时刻,A要往堆栈里push数据,而B则要从堆栈中pop数据: (1)操作之前,堆栈中有两个字符: data = | a | c | | | | | index = 2 (2)A执行push中的第一条语句dataindex = r: data = | a | c | r | | | |

17、 index = 2 (3)A还没有执行index+语句,A被B中断,B执行pop()方法,返回c: data = | a | c | r | | | | index = 1 (4)A继续执行index+语句: data = | a | c | r | | | | index = 2最后的结果是r并没有添加到堆栈中去。,14.5.1、多线程间的资源共享,例14.4 银行存取款线程设计,14.5.1、多线程间的资源共享,class Account1 /帐户缓冲区 private String name; private int value;int get(int i) /欲取金额i,返回实际取到

18、金额if (valuei)value = value - i; /取走时,value值减少else /帐户金额不够所取时 i = value;value = 0; /取走全部所余金额return i; ,14.5.1、多线程间的资源共享,class Account1 /帐户缓冲区 void put(int i) /欲存入金额i value = value + i; /存入时,value值增加int howmatch() /查看帐户上现有金额 return value; ,14.5.1、多线程间的资源共享,class Fetch1 extends Thread /取款线程 private Acc

19、ount1 a1;private int amount;public Fetch1(Account1 a1,int amount)this.a1 = a1 ;this.amount = amount;public void run()int k = a1.howmatch(); /察看余额try sleep(1); /花费时间 catch(InterruptedException e) System.out.println(e); System.out.println(“现有“+k+“, 取走“+a1.get(amount)+“, 余额“+a1.howmatch(); ,class Save1

20、 extends Thread /存款线程 private Account1 a1; private int amount;public Save1(Account1 a1,int amount) this.a1 = a1; this.amount = amount; public void run() int k = a1.howmatch(); /察看余额try sleep(1); /花费时间catch(InterruptedException e)System.out.println(e); a1.put(amount);System.out.println(“现有“+k+“, 存入“+

21、amount+“, 余额“+a1.howmatch();public static void main (String args) Account1 a1 = new Account1();(new Save1(a1,100).start(); (new Save1(a1,200).start();(new Fetch1(a1,500).start(); ,运行结果分析,在Java语言中,引入了“对象互斥锁”的概念(又称为监视器、管程)来实现不同线程对共享数据操作的同步。 用关键字synchronized来声明一个操作共享数据的方法或一段代码.,14.5.2、多线程间的资源共享处理同步处理,锁

22、定一段代码 Synchrinsized可锁定一段代码,称为创建一个“代码临界区”,一个时刻只能由一个线程访问,格式: Synchrinsized() 语句序列 当第一个线程执行这段代码时,它获得特定对象的使用权,即拥有该对象的锁。此时如果其它线程对同一个对象也要执行这段代码时。但该对象已经被锁定,它必须等候,直到锁被释放;,关键字synchronized,锁定一个方法 Synchrinsized 方法体 或, Synchrinsized(this) 方法体 多个线程对该方法访问,必须实现互斥访问; 一个时刻只能有一个线程访问它,关键字synchronized,本例采用synchrinsized

23、为锁定一段代码的方式来解决多线程的并发执行问题,在存款线程与取款线程创建代码临界区,形成对同一个账户对象“互斥”访问;,例14.5 带锁定的银行存取款线程设计,14.5.2、多线程间的资源共享同步处理,class Fetch2 extends Thread /取款线程 private Account1 a1; /账户对象成员 private int amount; /要取得款额public Fetch2(Account1 a1,int amount) this.a1 = a1 ;this.amount = amount;public void run() synchronized (a1) /

24、锁定帐户对象 int k = a1.howmatch(); try sleep(1); /花费时间catch(InterruptedException e)System.out.println(e);System.out.println(“现有“+k+“, 取走“+a1.get(amount)+“, 余额“+a1.howmatch(); ,class Save2 extends Thread /存款线程 private Account1 a1; private int amount;public Save2(Account1 a1,int amount) this.a1 = a1; this.

25、amount = amount;public void run() synchronized (a1) /锁定帐户对象 int k = a1.howmatch();try sleep(1); /花费时间catch(InterruptedException e) System.out.println(e); a1.put(amount);System.out.println(“现有“+k+“, 存入“+amount+“, 余额“+a1.howmatch();public static void main (String args) Account1 a1 = new Account1();(ne

26、w Save2(a1,100).start(); (new Save2(a1,200).start();(new Fetch2(a1,500).start(); ,运行结果分析,在存取款线程中设置了针对一个对象“互斥”访问的代码临界区, 每个线程运行前都要查看对象的锁定状态; 如果对象被锁定,必须等待,如果未被锁定则获得操作使用权, 以独占的方式运行临界区的代码,运行完毕则释放对该对象的 锁; 在任何时刻只有一个线程可以访问一个账户对象,并且可以可 保证在执行操作的过程中不被中断; 注意:线程sleep时放弃cpu,并未释放所拥有的对象锁,小结,实现线程有两种方法:实现Ruannable接口继

27、承Thread类 当新线程被启动时,Java运行系统自动调用该线程的run() 方法,它是Thread的核心,是执行对象 线程有五个基本状态:创建、可运行、运行中、不可运行、死亡 4. 线程优先级别的设置 5. 资源共享的同步问题synchronized,除了要处理多线程间共享数据操作的同步问题之外,在进行多线程程序设计时,还会遇到另一类问题,这就是如何控制相互交互的线程之间的运行进度,即多线程的同步。典型的模型:生产者消费者问题,若共享对象中只能存放一个数据,可能出现以下问题:生产者比消费者快时,消费者会漏掉一些数据没有取到;消费者比生产者快时,消费者取相同的数据。,14.5.3、数据共享的

28、同步处理,例11.6 发送线程与接收线程设计,本例使用两类线程:发送线程Sender1与接收线程Receiver1,它们 使用通一个缓冲区Buffer存放数据,有一个成员value存放值:put方法使获得value值;get方法返回value值,14.5.3、数据共享的同步,例14.7 发送线程与接收线程设计,class Buffer1 /缓冲区 private int value;void put(int i) value = i; int get() return value; class Receiver1 extends Thread /接受线程 private Buffer1 bf;

29、 /缓冲区对象public Receiver1(Buffer1 bf) this.bf = bf ;public void run( )for (int i=1; i6;i+)System.out.println(“tt Receiver get : “ + bf.get() ;try sleep(1); /取值后休息catch(InterruptedException e) System.out.println(e.getMessage(); ,class Sender1 extends Thread /发送线程 private Buffer1 bf;public Sender1(Buffe

30、r1 bf) this.bf = bf; public void run( ) for (int i=1;i6;i+) bf.put( i );System.out.println(“Sender put : “ + i );try sleep(1); /发送后休息catch(InterruptedException e) System.out.println(e.getMessage(); public static void main (String args) Buffer1 bf = new Buffer1();(new Sender1(bf).start(); (new Receiv

31、er1(bf).start(); ,运行结果分析,虽然发送Sender1线程与接收Receiver1线程每次休息一样,但 是由于系统调度线程的不确定性,造成Sender1线程发送数据与 Receiver1线程接收数据的不一致性: 即每次Sender1发送完数据休息,但是休息期间未必就调用 Receiver1线程接收数据,而是连续调用Sender1发送数据; 我们期望:每次Sender1发送数据的数据, Receiver1都可以接 收到; 必须增加方法锁定机制,才可以保证两线程运行的同步,用synchronized来标识的Buffer1类中的put与get方法: Synchronized voi

32、d put(i); Synchronized void get(i); 任一时刻只有一个线程可以访问 geit或put方法; 但有两个线程可以同时分别访问geit与put方法,还必须增加 “互斥锁标志”(lock flag)的信号量; Buffer2类中增加isEmpty成员,作为“互斥锁”标志: Value为空时, isEmpty值为true,可以赋值,不可以取值; Value为不空, isEmpty值为false,可以取值,不可赋值;,synchronized与互斥锁,为实现线程之间的通信,java.lang.Object类提供了相应方法: wait()方法 主动释放已持有的锁,进入互斥锁

33、等待队列; Sleep方法进入睡眠,但是不释放已持有的锁,这是它们 之间的区别; notify方法 唤醒wait队列中的第一个线程并把它移入锁申请队列; notifyAll方法 唤醒wait队列中的所有的线程并把它们移入锁申请队列; 需要指出的是: notify()/notifyAll()方法和wait ()方法都只能 在被声明为synchronized的方法或代码段中调用,线程通信,例14.7 带有互斥锁标志的发送线程与接收线程设计,Buffer类中增加isEmpty成员,作为“互斥锁”标志: value为空时, isEmpty值为true; value为不空, isEmpty值为false

34、; 将Buffer类中的put与get方法声明为互斥(synchronized) Put方法: isEmpty值为true,value为空,为value赋值,然 后唤醒( notify )一个等待接受线程; isEmpty值为false,value不空,不能为value赋值,只有等待(wait)值被取走后才可以赋值; get方法: isEmpty值为true,value为空,不能取value有效值,只有等待(wait)被赋值后才可以取; isEmpty值为false,value不空,可取value值,然后唤醒( notify )一个等待的发送线程,继续发送下一个值;,class Buffer

35、/加互斥锁的缓冲区 int value; boolean isEmpty = true; /value是否为空的信号量synchronized void put(int i) /value空时,为其赋值 while (!isEmpty) /当value不空时,等待try this.wait(); /等待 catch(InterruptedException e)System.out.println(e.getMessage();value = i; /当value空时,value获得值isEmpty = false; /设置value为不空状态notify(); /唤醒其他等待线程synchr

36、onized int get() /value不空时,取其值while (isEmpty) /当value空时,等待try this.wait();catch(InterruptedException e) System.out.println(e.getMessage();isEmpty = true; /设置value为空状态,并返回值notify(); /唤醒其他等待线程return value; /取得value值 ,public class Sender extends Thread /发送线程 private Buffer bf;public Sender(Buffer bf) t

37、his.bf = bf; public void run( ) /发送线程,向Buffer发5个值 for (int i=1;i6;i+) bf.put( i );System.out.println(“Sender put : “ + i );public static void main (String args) Buffer bf = new Buffer2();(new Sender(bf).start(); (new Receiver(bf).start(); class Receiver extends Thread /接受线程 private Buffer bf;public

38、Receiver(Buffer2 bf) this.bf = bf ; public void run() /接受线程,准备从Buffer中接受5个值 for (int i=1; i6;i+)System.out.println(“tt Receiver get : “ + bf.get() ; ,运行结果,实现了数据共享的同步,线程间的通信方式有两种:管道流和共享中间类; 两个或多个线程竞争资源时,需要用同步的方法协调 多个线程执行时,要用到同步方法,即使用synchronized的关键字设定同步区,即临界区,实现互斥访问;Wait()和notify()起协调作用;,小结,作业,创建两个线程的实例,分别将一个数组从小大大和从达到小排列。输出结果.,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报