1、第11章 多线程,本章重点 线程的生命周期 多线程技术 线程的创建和实现多线程 线程的同步,11.1多线程的基本概念,多线程机制是Java语言的又一重要特征,使用多线程技术可以使系统同时运行多个执行体,这样可以加快程序的响应时间,提高计算机资源的利用率。使用多线程技术可以提高整个应用系统的性能。,程序 - 进程 - 线程,程序是为完成特定任务、用某种语言编写的一组指令的集合。指一段静态的代码。 进程是程序的一次执行过程,是系统进行调度和资源分配的一个独立单位。,程序-进程-线程,线程是比进程更小一级的执行单元。 线程是一个程序中实现单一功能的一个指令序列,一个程序中可以包含若干个线程,形成多条
2、执行线索或者简单说,线程是轻量级的进程。 每个线程也有它自身的产生、存在和消亡的过程,也是一个动态的概念。 一个线程有它自己的入口和出口,以及一个顺序执行的序列 线程不能独立存在,必须存在于进程中,各线程间共享进程空间的数据。 线程 线程创建、销毁和切换的负荷远小于进程,又称为轻量级进程(lightweight process)。 系统负担小,主要是CPU的分配。,基本概念之一:进程,进程是正在运行的一个程序 程序:静态对象进程:动态过程 操作系统为每个进程分配一段内存空间,包括:代码、数据以及堆栈等资源 多任务的操作系统(OS)中,进程切换对 CPU资源消耗较大,多线程是这样一种机制,它允许
3、在程序中并发执行多个指令流,每个指令流都称为一个线程,线程彼此间互相独立。多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是无序执行的。线程和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信比进程简单。,基本概念之二:多线程技术,多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此也不用关心它,只需要设想各个线程是同
4、时执行即可。多线程的程序能更好地表述和解决现实世界的具体问题,是计算机应用开发和程序设计的一个必然发展趋势。,基本概念之二:多线程技术,进程与多线程,线程的生命周期,每个Java程序都有一个主线程,即main方法对应的线程。要实现多线程,必须在主线程中创建新的线程。在Java语言中,线程用Thread类及其子类的对象来表示。每个线程要经历由“新生就绪运行阻塞死亡”5种状态,线程从新生到死亡的状态变化过程称为生命周期。,线程的生命周期,新建,就绪,阻塞,运行,死亡,新建状态,用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通
5、过调用start方法进入就绪状态。,就绪状态,处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,因而将进入线程队列,等待系统为其分配CPU。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。,运行状态,在运行状态的线程执行自己的run方法中代码,直到调用其他方法而终止、或等待某资源而阻塞或完成任务而死亡。,阻塞状态,处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时终止自己的运行,进入阻塞状态。 在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状
6、态,重新到就绪队列中排队等待CPU资源。当再次获得CPU时,便从原来终止位置开始继续运行。,死亡状态,死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过执行stop或destroy方法来终止一个线程。,11.2 Java语言的Thread线程类与Runnable,在Java中,创建线程的方法有两种: 一种方法是通过创建Thread类的子类来实现; 另一种方法是通过实现Runable接口的类来实现。,10.2.1通过Runable接口实现多线程,通过Runable接口实现多线程的方法: 设计一个实现Runabl
7、e接口的类,然后根据工作需要重新设计线程的run方法;在run()中加入所要执行的代码建立该类的对象,以此对象为参数建立Thread类的对象; 调用Thread类对象的start方法启动线程,将执行权转交到run方法。,Java实例通过Runable接口,public class TestThread0 public static void main(String args) Runner0 r = new Runner0( ); Thread t = new Thread(r); t.start();/启动线程 for(int i=0; i100; i+) System.out.printl
8、n(“Main Thread:-“ + i); class Runner0 implements Runnable public void run() for(int i=0; i100; i+) System.out.println(“Runner0 :“ + i); ,11.2.2通过Thread类实现多线程,通过继承Thread类实现多线程的方法是首先设计Thread的子类,然后根据工作需要重新设计线程的run方法,在run()中加入所要执行的代码再使用start方法启动线程,将执行权转交到run。,Java实例通过Thread类,public class TestThread1 pub
9、lic static void main(String args) Runner1 r = new Runner1( ); r.start();/启动线程 for(int i=0; i100; i+) System.out.println(“Main Thread:-“ + i); class Runner1 extends Thread public void run() for(int i=0; i100; i+) System.out.println(“Runner1 :“ + i); ,线程等待,Java程序中的线程并发运行,共同争抢CPU资源。哪个线程抢夺到CPU资源后,就开始运行。
10、线程的调度执行是按照其优先级高低的顺序进行的,为了防止高级线程未完成,低级线程没有机会获得CPU,使低级线程有机会执行,那么让高级线程暂时休眠一段时间,使用sleep()方法,休眠时间的长短由sleep()方法中的参数决定。,Java实例通过Thread类(线程等待 ),class Thread1 extends ThreadString s;int m, count=0;Thread1(String ss, int mm) s=ss; m=mm; public void run()trywhile (true)System.out.print(s); sleep(m); /线程的休眠时间co
11、unt+;,Java实例(续),if (count = 20) break;System.out.println(s+“finished !“);catch(InterruptedException e) return;public static void main(String args)Thread1 threadA = new Thread1(“A “, 50);Thread1 threadB = new Thread1(“B “, 100);threadA.start();threadB.start();,Java实例通过Runable接口线程等待,class Thread2 impl
12、ements Runnable String s;int m, count=0;Thread2(String ss, int mm) s=ss; m=mm; public void run()trywhile (true)System.out.print(s);Thread.sleep(m);if (+count = 20) break;,Java实例(续),System.out.println(s+“has finished !“); catch (InterruptedException e) return;public static void main(String args)Threa
13、d2 threadA = new Thread2(“A “, 50);Thread2 threadB = new Thread2(“B “, 100);Thread threadC=new Thread(threadA); Thread threadD=new Thread(threadB); threadC.start();threadD.start();,Java实例线程等待,三个线程争夺资源 class Thread3 extends ThreadString s;int m, i=0;Thread3(String ss) s=ss; public void run()tryfor( i
14、=0; i6; i+) sleep(int)(500*Math.random(); System.out.println(s); ,Java实例(续),System.out.println(s+“finished !“);catch(InterruptedException e) return;public static void main(String args)Thread3 threadA = new Thread3(“A “);Thread3 threadB = new Thread3(“B “);threadA.start();threadB.start();System.out.p
15、rintln(“main is finished“);,Java实例(续),System.out.println(s+“finished !“);catch(InterruptedException e) return;public static void main(String args)Thread3 threadA = new Thread3(“A “);Thread3 threadB = new Thread3(“B “);threadA.start();threadB.start();System.out.println(“main is finished“);,继承Thread多个
16、线程之间不能不能实现资源的共享,而实现了Runnable接口之后可以实现多个线程的共享。,举例:火车票 售票点,100张票 1. class Demo extends Thread/继承Thread类 private int ticket=10;public void run()while(this.ticket0)System.out.println(“卖票“+this.ticket-); ,继承Thread多个线程之间不能不能实现资源的共享,而实现了Runnable接口之后可以实现多个线程的共享。,public class ThreadDemo04 public static void m
17、ain(String args)/准备4个售票点Demo d1=new Demo();Demo d2=new Demo();Demo d3=new Demo();Demo d4=new Demo();d1.start();d2.start();d3.start();d4.start(); ,运行结果,继承Thread多个线程之间不能不能实现资源的共享,而实现了Runnable接口之后可以实现多个线程的共享。,举例:火车票 售票点,100张票 2. class Demo1 implements Runnable/实现Runnable接口 private int ticket=10;public
18、void run()while(this.ticket0)System.out.println(“卖票“+this.ticket-); ,继承Thread多个线程之间不能不能实现资源的共享,而实现了Runnable接口之后可以实现多个线程的共享。,public class ThreadDemo05 public static void main(String args)/4个售票点应该控制同一资源:10Demo1 d=new Demo1();Thread t1=new Thread(d);Thread t2=new Thread(d);Thread t3=new Thread(d);Threa
19、d t4=new Thread(d);t1.start();t2.start();t3.start();t4.start(); ,运行结果,实验59,课堂练习:,10.2.3 线程同步,Java提供了多线程机制,通过多线程的并发运行可以提高系统资源利用率,改善系统性能。但在有些情况下,一个线程必须和其他线程合作才能共同完成任务。线程可以共享内存,利用这个特点可以在线程之间传递信息。 在Java中,实现同步操作的方法是在共享内存变量的方法前加synchronized修饰符。在程序运行过程中,如果某一线程调用经synchronized修饰的方法,在该线程结束此方法的运行之前,其他所有线程都不能运行
20、该方法,只有等该线程完成此方法的运行后,其他线程才能引入该方法的运行。,Java实例线程并发引发的不确定性,class Cbank private static int s=2000; public static void sub(int m)int temp=s;temp=temp-m;tryThread.sleep(int)(1000*Math.random();catch (InterruptedException e)s=temp;System.out.println(“s=“+s); ,Java实例线程并发引发的不确定性(续),class Customer extends Threa
21、dpublic void run()for( int i=1; i=4; i+)Cbank.sub(100); public class Thread51public static void main(String args)Customer customer1 = new Customer();Customer customer2 = new Customer();customer1.start();customer2.start(); ,运行结果,s=1900 s=1900 s=1800 s=1800 s=1700 s=1700 s=1600 s=1600,分析(线程并发引起的不确定性),
22、线程可以共享内存,利用这个特点可以在线程间传递信息。然而,对内存空间的共享可能会造成程序运行的不确定性和其它错误。 该程序的本意是通过两个线程分多次从一个共享变量中减去一定量值,以模拟从银行账户中的取款操作。,分析(线程并发引起的不确定性),4.存款额的初始值是2000,a如果每人各取400元,最后的存款应是1200元。但程序的运行结果并非如此。为什么?5.是由于customer1和customer2的并发运行引起的。例如,当customer1取钱时,如果其值是2000, customer1 中临时变量temp的初始值是2000,然后将temp的值改为1900,再将temp的新值写回s之前,
23、customer1 睡眠了一段时间。正在customer1 的睡眠时间内, customer2也读取 s的值,其值仍然是 2000,分析(线程并发引起的不确定性),5( 续) custome 2 中临时变量temp的初始值是2000,然后将temp的值改为1900,再将temp的新值写回s之前, customer2睡眠了一段时间.正在该段时间内, customer1睡眠 结束,将s更改为其temp的值 1900,并输出1900.,总结,1.通过对上一程序的分析,发现错误的根本原因是并发线程同时存取同一内存变量所引起的。后一线程对变量的更改结果覆盖了后前一线程对变量的更改结果。 2. 在Java
24、中,实现同步操作的方法是在共享内存变量的方法前加synchronized修饰符。在程序运行过程中,如果某一线程调用经synchronized修饰的方法,在该线程结束此方法的运行之前,其他所有线程都不能运行该方法,只有等该线程完成此方法的运行后,其他线程才能引入该方法的运行。,分析(线程并发引起的不确定性),4.类Cbank用来模拟银行账户,其中静态变量s表示账户的现有存款额,静态方法sub ()方法表示取款操作。方法sub()中的参数m表示每次的取款额。每次的取款操作过程是首先将将账户的现有存款额的值暂时存放在临时变量temp中,再从 temp中减去取款值m 。为了模拟银行取款过程中的网上阻塞
25、,让系统睡眠一段时间,再将 temp的值返回给s.最后显示新存款额。,Java实例线程同步,class Cbank private static int s=2000; public synchronized static void sub(int m)int temp=s;temp=temp-m;tryThread.sleep(int)(1000*Math.random();catch (InterruptedException e)s=temp;System.out.println(“s=“+s); ,Java实例(续),class Customer extends Threadpubli
26、c void run()for( int i=1; i=4; i+)Cbank.sub(100); public class Thread5public static void main(String args)Customer customer1 = new Customer();Customer customer2 = new Customer();customer1.start();customer2.start(); ,运行结果,s=1900 s=1800 s=1700 s=1600 s=1500 s=1400 s=1300 s=1200,分析,由于该程序在给 Cbank类中的方法su
27、b()方法前加了同步限制,修饰符synchronized.避免了一个线程对 s的修改结果覆盖了另一线程 对s 变量的覆盖结果。,作业,1.下列输出为一个多线程程序的一次执行结果,请编写一个程序实现。 Main Thread:-0 Runner1 :0 Runner1 :1 Main Thread:-1 Runner1 :2 Main Thread:-2 Main Thread:-3 Runner1 :3 2随便选择两个城市作为预选旅游目标。实现两个独立的线程分别显示10次城市名,每次显示后休眠一段随机时间(1000毫秒以内),哪个城市先显示完毕,就决定去哪个城市。分别用Runnable 接口和Thread类实现。 3编写程序实现线程的同步假设一个银行的ATM机,它可以允许用户存款也可以取款现在一个账户上有200元,用户A和用户B都拥有在这个账户上存款和取款的权利用户A将存入100元,而用户B将取出50元,那么最后账户的存款应该是250元。,