1、软件开发课程系列软件开发课程系列 本章主要讲述如下内容:本章主要讲述如下内容:多线程的概念;多线程的概念;线程的生命周期;线程的生命周期;多线程编程中的常量和方法;多线程编程中的常量和方法;线程调度方法;线程调度方法;资源冲突与协调;资源冲突与协调;线程之间的通信。线程之间的通信。19.1 简介简介9.1.1 什么是线程什么是线程一般来说一般来说,我们把正在计算机中执行的程序叫做我们把正在计算机中执行的程序叫做“进进程程”(Process),而不将其称为程序而不将其称为程序(Program)。所谓。所谓“线线程程”(Thread),是是“进程进程”中某个单一顺序的控制流。中某个单一顺序的控制流
2、。线程在程序中是独立的、并发的执行路径线程在程序中是独立的、并发的执行路径线程是进程中的一个实体,是被系统独立调度和分派的基本线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。程所拥有的全部资源。江西新华电脑学院江西新华电脑学院进程就是在某种程度上相互隔离的、独立运行的程序。进程就是在某种程度上相互隔离的、独立运行的程序。线程是进程内部的单一控制序列流。一个进程可以具有多个线
3、程是进程内部的单一控制序列流。一个进程可以具有多个并发的线程。并发的线程。进程可以支持多个线程,它们看似同时执行,但互相之间并进程可以支持多个线程,它们看似同时执行,但互相之间并不同步。不同步。(CPU只是将时间切割为时间片,然后将时间片分配给这只是将时间切割为时间片,然后将时间片分配给这些线程,些线程,CPU的高速计算能力,给人的感觉就像是多个线程在同时执的高速计算能力,给人的感觉就像是多个线程在同时执行一样。行一样。)进程中的所有线程共享进程的虚拟地址空间,这意味着所有进程中的所有线程共享进程的虚拟地址空间,这意味着所有线程都可以访问进程的全局变量和资源。这一方面为编程线程都可以访问进程的
4、全局变量和资源。这一方面为编程带来了方便,但另一方面也容易造成冲突。带来了方便,但另一方面也容易造成冲突。线程和进程线程和进程39.3 JAVA线程线程9.3.1 实现方式实现方式Java程序通过流控制来执行程序流,程序中单个顺序的流程序通过流控制来执行程序流,程序中单个顺序的流控制称为线程,多线程则指的是在单个程序中可以同时控制称为线程,多线程则指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务运行多个不同的线程,执行不同的任务 Java里面实现多线程,有里面实现多线程,有2个方法个方法继承Thread类(位于java.lang包内)实现Runnable接口一般鼓励使用第二种方法,
5、因为一般鼓励使用第二种方法,因为Java里面只允许单一继承,里面只允许单一继承,但允许实现多个接口。第二个方法更加灵活但允许实现多个接口。第二个方法更加灵活4 每个线程都是通过某个特定每个线程都是通过某个特定Thread对象所对象所对应的方法对应的方法run()来完成其操作的来完成其操作的 方法run()称为线程体 使用start()方法启动线程 启动线程是使线程进入到可运行启动线程是使线程进入到可运行(runnable)状态,并不一定立即开始执行该线程状态,并不一定立即开始执行该线程9.3 JAVA线程线程5public class HelloThread extends Thread pu
6、blic void run()System.out.println(I extend Thread.);Java实现线程,方法一:继承Thread继承Thread线程体,所有的操作都发生在线程体中public class TestHello public static void main(String args)HelloThread helloThread=new HelloThread();helloThread.start();start()方法启动线程运行结果:I extend Thread.Java实现线程,方法二:实现Runnable接口public class HelloRunn
7、er implements Runnable public void run()System.out.println(I implements“Runable.);实现Runnable线程体,所有的操作都发生在线程体中public class TestHello public static void main(String args)HelloRunner runner=new HelloRunner();Thread thread=new Thread(runner);thread.start();start()方法启动线程运行结果:I implement Runnable.启动线程是使线程
8、进入到可运行(runnable)状态,并不一定立即开始执行该线程实例化线程实例化线程6两种创建线程方法的比较两种创建线程方法的比较使用使用Runnable接口接口形成清晰的模型;还可以从其他类继承;保持程序风格的一致性。直接继承直接继承Thread类类不能再从其他类继承;编写简单,可以直接操纵线程7线程属性线程属性线程体线程体所有的操作都发生在线程体中,在Java中线程体是从Thread类继承的run()方法,或实现Runnable接口的类中的run()方法。当线程产生并初始化后,实时系统调用它的run()方法。run()方法内的代码实现所产生线程的行为,它是线程的主要部分。线程状态线程状态新
9、线程态可运行态运行中状态等待、阻塞、睡眠状态死亡态线程优先级线程优先级 89.3.4 线程状态线程状态新建状态、初始化状态:新建状态、初始化状态:线程对象已经被创建,但是还没有被启动时的状态。这段时间就是在我们调用new命令之后,调用start()方法之前。可运行状态、就绪状态:可运行状态、就绪状态:在我们调用了线程的start()方法之后线程所处的状态。处于RUNNABLE状态的线程在JAVA虚拟机(JVM)上是运行着的,但是它可能还正在等待操作系统分配给它相应的运行资源以得以运行。运行状态运行状态当线程调度器选择一个线程作为当前执行进程时,该线程就处于运行状态99.3.4 线程状态线程状态
10、等待、阻塞、睡眠状态等待、阻塞、睡眠状态当以下事件发生时,线程进入非运行态。suspend()方法被调用;sleep()方法被调用;线程使用wait()来等待条件变量;线程处于I/O等待DEAD(死亡状态):(死亡状态):当run()方法返回,或别的线程调用stop()方法,线程进入死亡态 10线程的执行特性线程的执行特性一个线程必须处于如下五种可能的状态之一:一个线程必须处于如下五种可能的状态之一:初始态/新状态Runnable运行中状态:阻塞/NonRunnable死状态11129.3.5 方方 法法功功 能能run()线程的执行逻辑线程的执行逻辑 start()启动线程启动线程 isAl
11、ive()判断线程是否还判断线程是否还“活活”着,即线程是否还未终止。着,即线程是否还未终止。getPriority()获得线程的优先级数值获得线程的优先级数值setPriority()设置线程的优先级数值设置线程的优先级数值yield()暂定线程的执行,允许其它线程竞争暂定线程的执行,允许其它线程竞争CPU,常用于具有同等,常用于具有同等优先级的线程之间的竞争,适用于不支持时间分片的操作系优先级的线程之间的竞争,适用于不支持时间分片的操作系统之间的线程处理统之间的线程处理 Thread.sleep()将当前线程睡眠指定毫秒数将当前线程睡眠指定毫秒数,sleep并不释放锁并不释放锁 stop(
12、)杀死一个线程,但是通常通过线程内的循环条件来结束一个杀死一个线程,但是通常通过线程内的循环条件来结束一个线程。线程。join()主线程等待子线程的终止。也就是在子线程调用了主线程等待子线程的终止。也就是在子线程调用了join()方方法后面的代码,只有等到子线程结束了才能执行。法后面的代码,只有等到子线程结束了才能执行。wait()当前线程进入对象的当前线程进入对象的wait pool。notify()/notifyAll()唤醒对象的唤醒对象的wait pool中的一个中的一个/所有等待线程。所有等待线程。13149.3.6 public class TestThreadStatus ext
13、ends Thread /构造函数 public TestThreadStatus(String name)super(name);public void run()try System.out.print(Thread.currentThread().getName();System.out.println(将进入暂停状态。现在时间:+new Date();Thread.sleep(2000);System.out.print(Thread.currentThread().getName()+的状态:);System.out.print(Thread.currentThread().getS
14、tate();System.out.println(现在时间:+new Date();catch(InterruptedException e)e.printStackTrace();sleep():非运行状态获得运行状态/main方法启动线程public static void main(String args)Thread t1=new TestThreadStatus(线程);t1.start();运行结果:线程将进入暂停状态。现在时间:Wed Dec 02 08:40:16 CST 2009线程的状态:RUNNABLE 现在时间:Wed Dec 02 08:40:18 CST 2009
15、15public class CounterThread extends Thread private int result;public CounterThread(String name)super(name);public int getResult()return result;public void run()try Thread.sleep(3000);System.out.println(Thread.currentThread().getName()+被阻塞了3000ms);catch(InterruptedException ex)System.out.println(ex.
16、getMessage();result=5;9.3.7 加入线程加入线程(join方法方法)16public class TestThreadJoin public static void main(String args)System.out.println(现在的线程是:+Thread.currentThread().getName();long start=System.currentTimeMillis();System.out.println(开始时间:+start);CounterThread ct1=new CounterThread(线程1);CounterThread ct2
17、=new CounterThread(线程2);ct1.start();ct2.start();long end=System.currentTimeMillis();System.out.println(结束时间:+start);System.out.println(join花费时间:+(end-start)+ms);int result=ct1.getResult()+ct2.getResult();System.out.println(计算结果:+result);线程1启动线程2启动运行结果:现在的线程是:main开始时间:1259717049841结束时间:1259717049841j
18、oin花费时间:0ms计算结果:0线程1被阻塞了3000ms线程2被阻塞了3000ms加入jion()方法public class TestThreadJoin public static void main(String args)System.out.println(现在的线程是:+Thread.currentThread().getName();long start=System.currentTimeMillis();System.out.println(开始时间:+start);CounterThread ct1=new CounterThread(线程1);CounterThrea
19、d ct2=new CounterThread(线程2);ct1.start();try ct1.join();catch(InterruptedException e)System.out.println(e.getMessage();ct2.start();try ct2.join();catch(InterruptedException e)System.out.println(e.getMessage();long end=System.currentTimeMillis();System.out.println(结束时间:+start);System.out.println(join
20、花费时间:+(end-start)+ms);int result=ct1.getResult()+ct2.getResult();System.out.println(计算结果:+result);Thread.join()让当前线程block住,等thread执行完之后,再继续执行。必须等2个线程都执行完才能汇总,那么这时候在主线程里面让2个线程join,最后计算结果既可运行结果:现在的线程是:main开始时间:1259717860458线程1被阻塞了3000ms线程2被阻塞了3000ms结束时间:1259717860458join花费时间:6022ms计算结果:10179.3.8 线程优先级
21、与调度线程优先级与调度线程的优先级用数字来表示,范围从线程的优先级用数字来表示,范围从1到到10,一个线程的缺,一个线程的缺省优先级是省优先级是5 Thread.MIN_PRIORITY=1Thread.MAX_PRIORITY=10Thread.NORM_PRIORITY=5int getPriority()方法获得线程对象的优先级方法获得线程对象的优先级setPriority(int newPriority)方法设置优先级方法设置优先级Java提供一个线程调度器来监控程序中启动后进入就绪状态提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪的
22、所有线程。线程调度器按照线程的优先级决定应调度哪些线程来执行些线程来执行多数线程的调度是抢先式的。即如果在当前线程执行过程中,多数线程的调度是抢先式的。即如果在当前线程执行过程中,一个更高优先级的线程进入可运行状态一个更高优先级的线程进入可运行状态,则这个线程立即则这个线程立即被调度执行被调度执行.189.3.9 共享受限资源共享受限资源多线程的出现,可以同时做多件事情。但是多线程的出现,可以同时做多件事情。但是当一个资源是受限制的资源的时候,多个线当一个资源是受限制的资源的时候,多个线程进行访问就会出现一些问题。必须防止这程进行访问就会出现一些问题。必须防止这类资源访问冲突的问题,否则就会出
23、现多个类资源访问冲突的问题,否则就会出现多个线程访问同一个银行账户,多个买家同时拍线程访问同一个银行账户,多个买家同时拍下一个宝贝之类的问题。下一个宝贝之类的问题。如何避免呢如何避免呢19使用资源的时候给它上把“锁”JAVA通过synchronized实现锁20同步线程同步线程synchronizedJava中的多线程使用中的多线程使用 synchronized关键字实现同步。关键字实现同步。为了避免线程中使用共享资源的冲突,当线程进为了避免线程中使用共享资源的冲突,当线程进入入 synchronized的共享对象时,将为共享对象加上的共享对象时,将为共享对象加上锁锁,阻止其他的线程进入该共享
24、对象。阻止其他的线程进入该共享对象。的问题:synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于可作用于instance变量、变量、object reference(对象引用对象引用)、static函数和函数和class literals(类名称字面常量类名称字面常量)身上。身上。无论无论synchronized关键字加在方法上还是对象上,它取得的锁都是对关键字加在方法上还是对象上,它取得
25、的锁都是对象,而不是把一段代码或函数当作锁象,而不是把一段代码或函数当作锁而且同步方法很可能还会被而且同步方法很可能还会被其他线程的对象访问。其他线程的对象访问。每个对象只有一个锁每个对象只有一个锁(lock)与之相关联。与之相关联。实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。尽量避免无谓的同步控制。21订票系统 public class TicketsThread implements Runnable int tickets=3;public void run()while(tickets
26、0)sale();public void sale()if(tickets 0)try Thread.sleep(1);catch(Exception e)System.out.println(e.getMessage();tickets-;/卖掉一张票System.out.println(Thread.currentThread().getName()+卖掉一张票,现在剩余:+tickets);剩余票数线程体售票业务售票窗口 public class TicketOffice public static void main(String args)TicketsThread t=new Ti
27、cketsThread();new Thread(t).start();new Thread(t).start();运行结果:Thread-0卖掉一张票,现在剩余:2Thread-1卖掉一张票,现在剩余:1Thread-1卖掉一张票,现在剩余:0Thread-0卖掉一张票,现在剩余:-122订票系统 public class TicketsThread implements Runnable int tickets=3;public void run()while(tickets 0)sale();public synchronized void sale()if(tickets 0)try
28、Thread.sleep(1);catch(Exception e)System.out.println(e.getMessage();tickets-;/卖掉一张票System.out.println(Thread.currentThread().getName()+卖掉一张票,现在剩余:+tickets);加入synchronized修饰符售票窗口 public class TicketOffice public static void main(String args)TicketsThread t=new TicketsThread();new Thread(t).start();ne
29、w Thread(t).start();运行结果:Thread-0卖掉一张票,现在剩余:2Thread-1卖掉一张票,现在剩余:1Thread-1卖掉一张票,现在剩余:023运行结果:Thread-0卖掉一张票,现在剩余:2Thread-1卖掉一张票,现在剩余:1Thread-1卖掉一张票,现在剩余:0Thread-0卖掉一张票,现在剩余:-1没有synchronized修饰比较两次运行结果运行结果:Thread-0卖掉一张票,现在剩余:2Thread-1卖掉一张票,现在剩余:1Thread-1卖掉一张票,现在剩余:0有synchronized修饰249.3.10 死锁死锁 因为线程可以阻塞,
30、并且对象可以具有同步控制方因为线程可以阻塞,并且对象可以具有同步控制方法,用以防止别的线程在锁还没有释放的时候就访法,用以防止别的线程在锁还没有释放的时候就访问这个对象。问这个对象。所以就可能出现这种情况:某个线程在等待另一个所以就可能出现这种情况:某个线程在等待另一个线程,而后者又在等待别的线程,这样一直下去,线程,而后者又在等待别的线程,这样一直下去,直到这个链条上的线程又在等待第一个线程的释放直到这个链条上的线程又在等待第一个线程的释放锁。锁。你将得到一个线程之间相互等待的连续循环,没有你将得到一个线程之间相互等待的连续循环,没有哪个线程能继续。这称之为哪个线程能继续。这称之为“死锁死锁
31、”(deadlock)。)。25死锁的四个必要条件:死锁的四个必要条件:互斥条件:一个资源每次只能被一个进程使互斥条件:一个资源每次只能被一个进程使用。用。请求与保持条件:一个进程因请求资源而阻请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。塞时,对已获得的资源保持不放。不剥夺条件不剥夺条件:进程已获得的资源,在末使用进程已获得的资源,在末使用完之前,不能强行剥夺。完之前,不能强行剥夺。循环等待条件循环等待条件:若干进程之间形成一种头尾若干进程之间形成一种头尾相接的循环等待资源关系。相接的循环等待资源关系。269.4 总结总结线程和进程的区别线程和进程的区别线程的优点线程的
32、优点JAVA线程的实现线程的实现线程的状态和调度线程的状态和调度多线程同步问题多线程同步问题死锁死锁279.1 Java中多线程的基本概念中多线程的基本概念 在多线程模型中,多个线程共存于同一块内在多线程模型中,多个线程共存于同一块内存中,且共享资源。存中,且共享资源。每个线程分配有限的时间片来处理任务。由每个线程分配有限的时间片来处理任务。由于于CPU在各个线程之间的切换速度非常快,在各个线程之间的切换速度非常快,用户感觉不到,从而认为并行运行。用户感觉不到,从而认为并行运行。289.1.1 多线程的特点多线程的特点 多个线程在运行时,系统自动在线程之间进多个线程在运行时,系统自动在线程之间
33、进行切换;行切换;由于多个线程共存于同一块内存,线程之间由于多个线程共存于同一块内存,线程之间的通信非常容易;的通信非常容易;Java将线程视为一个对象。线程要么是将线程视为一个对象。线程要么是Thread类的对象,要么是接口类的对象,要么是接口Runnable的的对象。对象。299.1.1 多线程的特点多线程的特点(续续)当多个线程并行执行时,具有较高优先级的当多个线程并行执行时,具有较高优先级的线程将获得较多的线程将获得较多的CPU时间片;时间片;优先级是从优先级是从1到到10的整数,并且它仅表示线的整数,并且它仅表示线程之间的相对关系;程之间的相对关系;多个线程共享一组资源,有可能在运行
34、时产多个线程共享一组资源,有可能在运行时产生冲突。必须采用生冲突。必须采用synchronized关键字协关键字协调资源,实现线程同步。调资源,实现线程同步。309.2.1 多线程编程中常用的常量和方法多线程编程中常用的常量和方法 线程类线程类Thread定义在定义在java.lang包中;包中;Thread类包含的常量有:类包含的常量有:1.public static final int MAX_PRIORITY:最大优先级,值是最大优先级,值是10。2.public static final int MIN_PRIORITY:最小优先级,值是最小优先级,值是1。3.public stati
35、c final int NORM_PRIORITY:缺省优先级,值是缺省优先级,值是5。319.2.1 多线程编程中常用的常量和方法多线程编程中常用的常量和方法(续续)常用方法:常用方法:currentThread():返回当前运行的线程对象,是一返回当前运行的线程对象,是一个静态的方法。个静态的方法。sleep(int n):使当前运行的线程睡使当前运行的线程睡n个毫秒,然后继个毫秒,然后继续执行,也是静态方法。续执行,也是静态方法。yield():使当前运行的线程放弃执行,切换到其它使当前运行的线程放弃执行,切换到其它线程,是一个静态方法。线程,是一个静态方法。isAlive():判断线程
36、是否处于执行的状态,返回值判断线程是否处于执行的状态,返回值true表示处于运行状态,表示处于运行状态,false表示已停止。表示已停止。329.2.1 多线程编程中常用的常量和方法多线程编程中常用的常量和方法(续续)start():使调用该方法的线程开始执行。使调用该方法的线程开始执行。run():该方法由该方法由start()方法自动调用。方法自动调用。stop():使线程停止执行,并退出可执行状态。使线程停止执行,并退出可执行状态。suspend():使线程暂停执行,不退出可执行态。使线程暂停执行,不退出可执行态。resume():将暂停的线程继续执行。将暂停的线程继续执行。setNam
37、e(String s):赋予线程一个名字。赋予线程一个名字。getName():获得调用线程的名字。获得调用线程的名字。339.2.1 多线程编程中常用的常量和方法多线程编程中常用的常量和方法(续续)getPriority():获得调用线程的优先级。获得调用线程的优先级。setPriority(int p):设置线程的优先级。设置线程的优先级。join():等待线程死亡,若中断了该线程,等待线程死亡,若中断了该线程,将抛出异常。将抛出异常。349.2.1 多线程编程中常用的常量和方法多线程编程中常用的常量和方法(续续)注意注意1 1:在创建线程对象时,缺省的线程优先在创建线程对象时,缺省的线程
38、优先级是级是5,一般设置优先级,一般设置优先级4到到6之间,不要设之间,不要设置为置为10,否则其它线程将执行不到。,否则其它线程将执行不到。注意注意2 2:Java的调度器能使高优先级的线程始的调度器能使高优先级的线程始终运行,一旦终运行,一旦CPUCPU有空闲,具有同等优先级有空闲,具有同等优先级的线程,以轮流的方式顺序使用时间片。的线程,以轮流的方式顺序使用时间片。359.2.2 线程的生命周期线程的生命周期 resume suspend wait stop I/O 请求结束 start notify notifyAll I/O 请求 睡眠时间结束 sleep 分配时间片 时间片结束 创
39、建 就绪 运行 结束 挂起 阻塞 睡眠 等待 36class getThreadInfo /程序程序9-1:单线程示例:单线程示例 public static void main(String args)String name;int p;Thread curr;curr=Thread.currentThread();System.out.println(当前线程当前线程:+curr);name=curr.getName();p=curr.getPriority();System.out.println(线程名线程名:+name);System.out.println(优先级优先级:+p);程
40、序输出结果:程序输出结果:当前线程当前线程:Threadmain,5,main线程名线程名:main优先级优先级:5 9.2.3 创建多线程的方法创建多线程的方法 方法方法1 1:通过通过Thread类的子类实现多线程。类的子类实现多线程。方法方法2 2:定义一个实现定义一个实现Runnable接口的类实接口的类实现多线程。现多线程。399.2.3 创建多线程的方法创建多线程的方法(续续)方法方法1 1:通过创建通过创建Thread类的子类实现多线类的子类实现多线程,步骤如下程,步骤如下:1.定义定义Thread类的一个子类。类的一个子类。2.定义子类中的方法定义子类中的方法run(),覆盖父
41、类中的,覆盖父类中的 方法方法run()。3.创建该子类的一个线程对象。创建该子类的一个线程对象。4.通过通过start()方法启动线程。例如方法启动线程。例如9-2:40/程序程序9-2class UserThread extends Thread int sleepTime;public UserThread(String id)/构造函数构造函数super(id);sleepTime=(int)(Math.random()*1000);System.out.println(线程名:线程名:+getName()+,睡眠:,睡眠:+sleepTime+毫秒毫秒);public void ru
42、n()try /通过线程睡眠模拟程序的执行通过线程睡眠模拟程序的执行 Thread.sleep(sleepTime);catch(InterruptedException e)System.err.println(运行异常运行异常:+e.toString();System.out.println(运行的线程是:运行的线程是:+getName();public class multThreadTestpublic static void main(String args)UserThreadt1,t2;t1=new UserThread(NO 1);t2=new UserThread(NO 2)
43、;t1.start();t2.start();程序某次的运行结果:程序某次的运行结果:线程名:线程名:NO 1,睡眠:,睡眠:885 毫秒毫秒线程名:线程名:NO 2,睡眠:,睡眠:66 毫秒毫秒目前运行的线程是:目前运行的线程是:NO 2目前运行的线程是:目前运行的线程是:NO 1 注意:注意:Thread类中的类中的run()方法具有方法具有public属属性,覆盖该方法时,前面性,覆盖该方法时,前面必须带上必须带上public。9.2.3 创建多线程的方法创建多线程的方法(续续)方法方法2 2:通过接口创建多线程,步骤如下:通过接口创建多线程,步骤如下:1.定义一个实现定义一个实现Run
44、nable接口的类。接口的类。2.定义方法定义方法run()。Runnable接口中有一个空接口中有一个空的方法的方法run(),实现它的类必须覆盖此方法。,实现它的类必须覆盖此方法。3.创建该类的一个线程对象,并将该对象作参创建该类的一个线程对象,并将该对象作参数,传递给数,传递给Thread类的构造函数,从而生成类的构造函数,从而生成Thread类的一个对象。类的一个对象。/注意这一步注意这一步!4.通过通过start()方法启动线程。例如方法启动线程。例如9-3:45/程序程序9-3class UserMultThread implements Runnableint num;UserM
45、ultThread(int n)num=n;public void run()for(int i=0;i3;i+)System.out.println(运行线程:运行线程:+num);System.out.println(结束结束:+num);public class multThreadZero public static void main(String args)throws InterruptedException Thread mt1=new Thread(new UserMultThread(1);Thread mt2=new Thread(new UserMultThread(2
46、);mt1.start();mt2.start();mt1.join();/等待线程死亡等待线程死亡mt2.join();程序运行某次的输出结果:程序运行某次的输出结果:运行线程:运行线程:1运行线程:运行线程:2运行线程:运行线程:1运行线程:运行线程:2运行线程:运行线程:1运行线程:运行线程:2结束结束:1结束结束:29.2.3 创建多线程的方法创建多线程的方法(续续)程序程序9-3中需要注意的中需要注意的2点:点:1.mt1.join()是等待线程死亡,对该方法必是等待线程死亡,对该方法必须捕捉异常,或通过须捕捉异常,或通过throws关键字指明可关键字指明可能要发生的异常。能要发生的
47、异常。2.对一个线程不能调用对一个线程不能调用start()两次,否则会两次,否则会产生产生IllegalThreadStateException异常。异常。499.3.1 线程调度模型线程调度模型 线程调度程序挑选线程时,将选择处于就绪线程调度程序挑选线程时,将选择处于就绪状态且优先级最高的线程。状态且优先级最高的线程。如果多个线程具有相同的优先级,它们将被如果多个线程具有相同的优先级,它们将被轮流调度。轮流调度。程序程序9-4验证了验证了Java对多线程的调度方法。对多线程的调度方法。50class threadTest extends Thread /程序程序9-4threadTest(
48、String str)super(str);public void run()tryThread.sleep(2);catch(InterruptedException e)System.err.println(e.toString();System.out.println(getName()+getPriority();public class multTheadOnepublic static void main(String agrs)Thread one=new threadTest(one);Thread two=new threadTest(two);Thread three=ne
49、w threadTest(three);one.setPriority(Thread.MIN_PRIORITY);two.setPriority(Thread.NORM_PRIORITY);three.setPriority(Thread.MAX_PRIORITY);one.start();two.start();three.start();程序输出结果:程序输出结果:three 10two 5one 1思考:思考:在在run()方法中,通过线程方法中,通过线程睡眠睡眠2个毫个毫秒,模拟程序的执行。秒,模拟程序的执行。如果不睡眠,你知道如果不睡眠,你知道可能的输出结果是什么吗?为什么?可能的输
50、出结果是什么吗?为什么?9.3.2 资源冲突资源冲突 多个线程同时运行虽然可以提高程序的执行多个线程同时运行虽然可以提高程序的执行效率,但由于共享一组资源,可能会产生效率,但由于共享一组资源,可能会产生冲突,例如程序冲突,例如程序9-5 9-5。54class UserThread /程序程序9-5 void Play(int n)System.out.println(运行线程运行线程 NO:+n);tryThread.sleep(3);catch(InterruptedException e)System.out.println(“线程异常线程异常,NO:+n);System.out.pri