《第五章 多线程编程.ppt》由会员分享,可在线阅读,更多相关《第五章 多线程编程.ppt(24页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、第五章线程与多线程线程与多线程线程相关概念程序是一段静态的代码,它是应用软件执行的蓝本。进程是程序的一次动态执行过程,它对应从代码加载、执行到执行完毕的一个完整过程。线程是比进程更小的执行单位。一个进程在其执行过程中,可以产生多个独立运行的子任务,形成多条执行线索,每个子任务都叫一个线程。线程可以由操作系统内核控制,也可以由用户程序控制。一个进程中可以包含多个线程,每个进程至少包含一个线程,所以会自动创建一个线程,这个线程通常称为主线程。线程和多线程线程是一个相当小的对象,它不会占用大量内存而且可以快速创建。因此,一个应用程序可以使用多个线程执行不同的任务。与进程不同的是,同类多线程共享同一块
2、内存空间和一组系统资源,所以,系统创建多线程花费单价较小。因此,也称线程为轻负荷进程。每个线程分配有限的时间片来处理任务。由于CPU在各个线程之间的切换速度非常快,用户感觉不到,从而认为并行运行。多线程和传统的单线程在设计上最大的区别在于:由于各个线程的控制流彼此独立,使得各线程之间代码执行的顺序不确定,由此带来线程调度、同步等问题。线程的创建方法1:通过编写Thread类的子类实现多线程。方法2:如果已经继承其他类,可以通过定义一个实现Runnable接口的类实现多线程。方法1,步骤如下:从Thread类派生一个类,覆盖Thread类中的run方法。例如:public class T ext
3、ends Thread public run()/需要以线程方式运行的代码 创建该派生类的对象。例如:T newThread=new T();调用start方法启动该线程。例如:newThread.start();class subThread1 extends Thread/线程线程1 public void run()/重写重写run()方法,输出方法,输出126for(int i=1;i=26;i+)System.out.println(i);sleep(100);public static void main(String args)new subThread1(1).start();
4、方法2,步骤如下:定义一个类,实现Runnable接口。例如:public class T implements Runnable public void run()/线程体 创建自定义类的对象。例如:T target=new T();创建Thread类对象,指定该类的对象作target参数。Thread newthread=new Thread(target);启动线程。例如:newthread.start();使用Runnable接口示例创建一个程序实现当前时间显示,要求每2秒显示1次,显示20次后程序退出。分析:在Java的java.util包中有一个Date类,可以通过实例化一个Dat
5、e对象得到当前时间。要求每两秒显示一次时间,线程是完成这个任务最好的工具。通过每两秒唤醒线程一次,并且就在这一瞬间显示出时间。还需设置一个循环控制输出次数。程序如下所示:public class ThreadDemo implements Runnable private Date nowtime;public void run()while(true)nowtime=new Date();System.out.println(nowtime);clock.sleep(2000);public static void main(String args)ThreadDemo t=new Thre
6、adDemo();Thread time=new Thread(t);t.start();线程的状态和生命周期每个Java程序都有一个缺省的主线程。对于Java应用程序,主线程是main方法执行的指令序列;要想实现多线程,必须创建新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,新建的线程在它的一个完整的生命周期中通常包括新建、可运行、阻塞和死亡四种状态。新建新建可运行可运行阻塞阻塞死亡死亡线程的生存周期状态图线程生命周期状态图返回线程的几种状态新建(New)状态:线程在产生时便进入新建状态。即new Thread()创建对象时,线程所处的便是这个状态,但此时系统并不会分配
7、资源,直到用start()方法激活线程时才会分配。可运行(Runnable)状态:处于新建状态的线程被启动(调用start方法)后,进入Runnable状态。Runnable状态可分为就绪和运行两种状态,但从程序设计的角度来看,可以认为它们是同一种状态。阻塞(Not Running)状态:当发生下列事件之一时,线程就进入阻塞状态。n该线程本身调用sleep()方法。sleep(long millis)可用来设置睡眠的时间。n线程调用对象的wait()方法;n该线程调用了suspend()方法;n输入输出流中发生线程阻塞;当线程被阻塞后,便停止run()方法的运行,直到被阻塞的原因不存在后,线程
8、回到可运行状态,继续排队争取CPU的资源。对于上面四种阻塞情况,都有可以返回可运行状态的方法与之对应。n线程进入睡眠状态,但指定的睡眠时间到了。n如果线程是由调用对象的wait()方法所阻塞,则该对象的notify()方法被调用时可解除阻塞。notify()方法用来“通知”被wait()阻塞的线程开始运行。n如果线程是由调用suspend()方法所阻塞,只能由其它线程调用它的resume()方法恢复n输入输出流中发生线程阻塞;死亡状态:处于死亡状态的线程不具有继续运行的能力。线程死亡的原因有两个:一个是正常运行的线程完成它的全部工作后退出;另一个则是线程被强制性地终止,如通过执行stop方法或
9、destroy来终止线程。此时,线程对象可以由JVM进行垃圾收集。利用上述方法可以完成线程间的通信以及线程状态之间的转换,下面举例说明通过wait()、notify()来达到线程间的通信和状态的转换。状态图线程间通信的示例:本例模拟两个银行之间的转帐业务,当A银行转帐给B银行后,暂停转帐,通知B银行转帐给A银行,转帐金额随机确定。public static final int MAX_PRIORITY:最大优先级,值是10。public static final int MIN_PRIORITY:最小优先级,值是1。public static final int NORM_PRIORITY:缺
10、省优先级,值是5。线程类Thread常用的常量Thread 类中的常用方法方法用途public static int activeCount();返回线程组中当前活动的线程数。public final String getName()返回线程的名称。public final int getPriority()返回线程优先级。public final boolean isAlive()判断当前线程是否激活的。final void setName(String name)将线程的名称设置为由name指定的名称 final void join()throws InterruptedException
11、等待线程死亡,若中断了该线程,将抛等待线程死亡,若中断了该线程,将抛出异常。出异常。方法用途void start()调用run方法启动线程,开始线程的执行。public void run()用于指定线程需要运行的代码,该方法由start()方法自动调用。public final void suspend()使线程暂停执行,不退出可执行状态。static void sleep(long millis)用于将线程睡眠一段时间,时间一过,线程重新进入执行状态。static void yield()使当前运行的线程放弃执行,切换到其它使当前运行的线程放弃执行,切换到其它线程,但不进入睡眠状态,仍处于可
12、执行线程,但不进入睡眠状态,仍处于可执行状态。状态。public void destroy()终止一个线程。终止一个线程。多线程同步透支透支withdrwal()withdrwal()余额余额线程线程1线程线程2线程线程10资源资源取过来取过来加加1后送回去后送回去变量变量多线程同步示例class Countmoney private static int sum=0;public static void add(int n)int t=sum;t=t+n;try Thread.sleep(int)(1000*Math.random();catch(InterruptedException e
13、)sum=t;System.out.println(sum=+sum);class Cperson extends Thread public void run()for(int i=1;i=3;i+)Countmoney.add(100);public class MoneyDemo public static void main(String args)Cperson c1=new Cperson();Cperson c2=new Cperson();c1.start();c2.start();运行结果:运行结果:sum=100sum=200sum=300sum=100sum=200sum
14、=300 同一进程的多个线程共享同一存储空间,带来同一进程的多个线程共享同一存储空间,带来了访问冲突这个严重的问题了访问冲突这个严重的问题。例如,两个线程访问同一个对象,一个线程向对象中存储数据,另一个线程读取该数据。如果第一个线程还没有完成存储操作第二个线程就开始读数据,就产生了混乱。因此,必须采用同步机制来防止类似情况发生,即在第一个线程完成存储操作之前,禁止其他线程访问该对象。同步的概念为了解决这个问题,为了解决这个问题,JavaJava语言提供了同步机制语言提供了同步机制来解决这种冲突,避免同一个数据对象被多来解决这种冲突,避免同一个数据对象被多个线程同时访问。个线程同时访问。Java
15、Java语言通过关键字语言通过关键字synchronizedsynchronized实现同步。实现同步。当对一个对象当对一个对象(含方法含方法)使用使用synchronizedsynchronized,这,这个对象便被锁定或者说进入了监视器。在一个对象便被锁定或者说进入了监视器。在一个时刻只能有一个线程可以访问被锁定的对个时刻只能有一个线程可以访问被锁定的对象。它访问结束时,让高优先级并处于就绪象。它访问结束时,让高优先级并处于就绪状态的线程,继续访问被锁定的对象,从而状态的线程,继续访问被锁定的对象,从而实现资源同步。实现资源同步。同步机制 关键字关键字synchronizedsynchro
16、nized用于声明在任何时候都只能有一个线程可以用于声明在任何时候都只能有一个线程可以执行一段代码或一个方法。执行一段代码或一个方法。synchronizedsynchronized有两种用法:有两种用法:synchronizedsynchronized方法(锁定冲突的方法)方法(锁定冲突的方法)和和synchronizedsynchronized块(锁定冲突的对象)。块(锁定冲突的对象)。通过在方法声明中加入通过在方法声明中加入synchronizedsynchronized关键字来声明同步方法。如:关键字来声明同步方法。如:synchronized synchronized 当一个线程调用
17、一个当一个线程调用一个“互斥互斥”方法时,它试图获得该方法锁。如方法时,它试图获得该方法锁。如果方法未锁定,则获得使用权,以独占方式运行方法体,运行果方法未锁定,则获得使用权,以独占方式运行方法体,运行完释放该方法的锁。如果方法被锁定,则该线程必须等待,直完释放该方法的锁。如果方法被锁定,则该线程必须等待,直到方法锁被释放时。到方法锁被释放时。wait-notify 机制为避免轮流检测,Java提供了一个精心设计的线程间通信机制,使用wait()、notify()和notifyAll()方法。这三个方法必须在已经持有锁的情况下执行,所以它们只能出现在synchronized作用的范围内。这些方法是作为 Object 类中的 final 方法实现的。wait()方法告知被调用的线程释放已持有的锁,并进入等待状态,直到其他线程进入相同的监视器并调用 notify()方法。notify()方法唤醒同一对象上第一个调用 wait()的线程。notifyAll()方法通知调用 wait()的所有线程,具有最高优先级的线程将先运行。