10.1 多线程的概念
10.1.1 程序、进程和多任务
程序:是对数据描述与操作的代码的集合,是应用程序执行的脚本
进程:是程序的一次执行过程,程序是静态的,进程是动态的。系统运行一个程序就是一个进程从创建、运行到消亡的过程。
多任务:一个系统中可以同时运行多个程序。一个CPU同时只能执行一个程序的一条指令,多任务运行的并发机制使这些任务交替运行。
10.1.2 线程
线程也称为轻型进程(LWP),是比进程更小的运行单位,一个进程可以被划分成多个线程。当一个程序执行多线程时,可以运行两个或多个由同一个程序启动的任务。这样一个程序可以使得多个活动任务同时发生。
10.1.3 多线程
与进程不同的是,同类多线程共享一块内存空间和一组系统资源,所以系统创建多线程开销相对较小。
10.1.4 线程的生命同期与Java的多线程机制
1.线程的生命同期与状态
线程有创建(New)、可运行(Runnable)、运行中(Running)、挂起(Not Runnable)、死亡(Dead)5种状态。
2.Java的多线程机制
java.lang中的线程类Thread封装了所有需要的线程操作控制,有很多方法用来控制一个线程的运行、休眠、挂起或停止。
10.2 创建线程
建立一个线程需要完成三件事:
> 建立一个虚拟的CPU
> 给出线程的执行代码
> 提供代码所操作的数据
两种方法可以创建线程:一种方法是通过继承线程类Thread来创建线程类;另一种方法是建立一个实现Runnable接口的类一创建线程 。
10.2.1 通过继承Thread类创建线程
继承Thread类这种方法中,需要覆盖run( )方法来提供线程的执行代码,定义Thread类的成员变量来提供线程的数据。线程执行时,从它的run( )方法中开始执行,run()方法是线程执行的起点
例:public class SC extends Thread{
int count=1,number;
SC(int num){
number=num;
System.out.println("创建线程"+number); }
public void run(){
while(true){
System.out.println("线程"+number+":计数"+count);
if(++count==3) return; } }
public static void main(String args[]){
for (int i=0;i<3;i++)
new SC(i+1).start(); } }
运行结果:创建线程1
创建线程2
创建线程3
线程1:计数1
线程1:计数2
线程2:计数1
线程2:计数2
线程3:计数1
线程3:计数2
例:P208
public class SC extends Thread{
public static void main(String args[]){
testThread t1=new testThread("线程1");//仅是一空线程对象,没有启动这一线程,系统不为它分配资源,
testThread t2=new testThread("线程2");
t1.start(); t2.start(); } } //启动线程
class testThread extends Thread{
public testThread(String str)
{super(str);} //调用父类构造方法为线程对象命名
public void run(){
for (int i=0;i<3;i++){
System.out.println(getName()+"在运行"); //getName返回线程名称
try
{sleep(1000); //用休眠1000毫秒来区分哪个线程在运行
System.out.println(getName()+"在休眠");}
catch(InterruptedException e){} }
System.out.println(getName()+"已结束"); }}
说明:由继承Thread创建的子类,必须覆盖run方法,因为run方法是abstact抽象方法
10.2.2 通过Runnable接口创建线程
如果是Applet类应不能再继承Thread类(不能多继承),这时可以通过接口Runnable直接创建线程对象。接口中只声明了一个未实现的run方法。
线程体的构造方法:
public Thread([ThreadGroup group][,Runnable target][,String name])
其中group指明该线程所属的线程组,target是执行线程体的目标对象,它必须实现接口Runnable,name则为线程名。这三个参数均可任意没有。
任何实现接口Runnable的对象都可以作为一个线程的目标对象,类Thread本身也实现了接口Runnable,因此我们可以通过两种方法实现线程体。
(1)定义一个线程类,它继承类Thread并重写其中的方法run(),这时在初始化这个类的实例时,目标对象target可为null
(2)提供一个实现接口Runnable类作为线程的目标对象,在初始化一个Thread类或者Thread子类的对象时,把目标对象传递给这个线程实例,由该目标对象提供线程体run()。这时实现接口Runnable的类仍然要以继承其他父类。
例:P210
import java.awt.*;
import java.applet.Applet;
import java.util.*;
import java.text.DateFormat;
public class SC extends Applet implements Runnable{
Thread clockThread=null;
public void init(){
setBackground(Color.blue);
setForeground(Color.yellow); }
public void start(){
if(clockThread==null){
clockThread=new Thread(this,"Clock2");
//Thread(ThreadGroup group,String name)name是线程名,group指定线程所属的组
clockThread.start(); } }
public void run(){
Thread myThread=Thread.currentThread();
//找到当前执行的线程,返回它的引用
while(clockThread==myThread){
repaint();
try{ Thread.sleep(1000);
} catch(InterruptedException e){}
} }
public void paint(Graphics g){
Date date=new Date();
DateFormat formatter=DateFormat.getTimeInstance();
String str=formatter.format(date);
g.drawString(str,5,10); }
public void stop()
{clockThread=null;} }
10.2.3 可运行状态(Runnable)
Thread MyThread=new Thread()
MyThread.start();
这样该线程处于可运行(Runnable)状态,注意,这状态并不是运行中状态(Running),因为线程也许实际并未真正运行(因很多计算机是单CPU)。当一个线程正在运行时,它是可运行的,并也是当前正运行的线程
10.2.4 不可运行状态(Not Runnable)
当下面四种情况发生时,线程就是进入不可运行状态:
(1)调用了sleep()方法
(2)调用了suspend()方法
(3)为等候一个条件变量,线程调用wait()方法
(4)输入输出流中发生线程阻塞
我们来看看下面这个例子:
Thread myThread=new Thread();
MyThread.start();
try{ myThread.sleep(10000);}
catch(InterrupedException e){
}
对于上面四种情况,都有特定的返回可运行状态的方法与之对应,对应方法如下:
(1)如果线程处于睡眠状态中,sleep()方法中的参数为休息时间,当时间过去后,线程即为可运行的。
(2)如果一线程被挂起,须由其他线程调用resume()方法来恢复该线程的执行。
(3)如果线程在等待条件变量,那么要停止等待的话,要该条件变量所在的对象调用notify(),notifyAll()方法。
(4)如果在I/O流中发生线程阻塞,则特定的I/O指令将结束这种不可运行状态
10.2.5 死亡状态(Dead)
一般可通过两种方法实现:自然撤消或是被停止
1.自然撤消
public void run(){
int i=0;
while(i<100){
i++;
System.out.println(“i=”+i); }}
当run()方法结束后,该线程应自然撤消了
2.被停止
Thread myThread=new Thread();
MyThread.start();
try{Trhead.currentThread().sleep(10000);
} catch(InterruptedException e)
myThread.stop();
10.2.6 非法状态处理(IllegalThreadStateException)
当一个线程刚被创建后,只能对它调用start()或stop()方法,若调用其他方法则会引起非法状态处理。同样对于任何状态,如果所调用的方法与状态不符,都会引起非法状态处理。
10.3 线程的优先级
Java为使有些线程可以提前得到服务,可给线程设置优先级。在单个CPU上运行多线程时采用了线程队列技术,Java虚拟机支持固定优先级队列,一个线程的执行顺序取决于其对其他Runnable线程的优先级。优先级分1~10同,默认级别是5级。
Thread的公用静态常量表示:
public static final int MAX_PRORITY=10 (最大优先级10)
public static final int MIN_PRORITY=1 (最小优先级10)
public static final int NORM_PRORITY=5 (默认优先级5)
public final int getPriority( ) //获得线程优先级
public final setPriority(int newPriority) //设置线程优先级
10.4 线程的调度与控制
10.4.1 线程类的方法
1.线程的类方法
Thread类的静态方法,即可直接从Thread类调用
CurrentThread( ):返回正在运行的Thread对象名称
sleep(int n):让当前线程休眠n毫秒
2.实例方法P213
10.4.2 控制线程的状态
1.挂起一个线程:suspend( )方法
t.suspend():将t暂停执行,必须由其他线程调用t.resume()恢复,不提倡使用该方法,因为容易造成死锁
2.停止一个线程:stop( )方法
当线程完成运行并结束后,将不能再运行。另不可用t.stop()强行终止线程。注:这并没有消灭这个线程,只是停止线程的执行。但这个线程不能用t.start()重新启动。不提倡采用这种方法,易造成线程的不一致。要通过设置flag通知一个线程应该结束。
3.线程休眠:sleep(long )方法
Thread.sleep()使一个线程暂停运行一段固定时间。
4.连接线程:join( )方法
t.join()使当前的线程等待,直到t结束为止,线程恢复到可运行状态。三种调用格式:
(1)join():如当前线程发出调用t.join(),则当前线程将等待线程t结束后再继续执行。
(2)join(long millis):如当前线程发出调用t.join(long millis),则当前线程将等待线程t结束或最多等待mills毫秒后再继续执行。
(3)join(long millis,int nanos):如当前线程发出调用t.join(long millis,int nanos),则当前线程将等待线程t结束或最多等待mills毫秒+nanos纳秒后再继续执行。
5.暂停(退让)线程:yield( )方法(只让给同优先级运行)
调用t.yield()方法可暂停当前运行线程,但处于可运行状态,让同优先级线程先运行。若没有同等优先级的线程是可运行状态,yield方法将什么也不做。
6.中断线程:interrupt( )方法
如果一个线程t在调用sleep(),join(),wait()方法被阻塞时,则t.interrupt()方法将使t的中断状态被清除,中断t的阻塞状态,并且将接收到InterruptException异常。
一个线程可以通过获取另一个线程的引用,调用该线程的interrupt()方法来中断另一个sleep或wait线程的执行
7.了解线程的状态:isAlive( )方法
返回true则线程处于Runnable或Not Runnable状态(即已启动但还没有运行结束),返回false则说明线程处于New Thread或Dead状态
start() 调度
时间片到yield() run(),stop()结束
时间到 sleep()
interrupt() join() wait()
synchronized()
获得互斥
使用权
notify()
notifyall()
线程状态的转换图
例:public class SC{
public static void main(String args[]) throws Exception{
int i=0;
Hello t=new Hello();
t.start();
while(true){
System.out.println("早上好!"+i++);
if (i==2&&t.isAlive()){
System.out.println("Main waiting for Hello!");
t.join(); //等待t运行结束
}
if (i==4) break; }}}
class Hello extends Thread{
int i;
public void run(){
while (true){
System.out.println("嗨"+i++);
if(i==4) break; } } }
运行结果:
早上好!0
早上好!1
Main waiting for Hello!
嗨0
嗨1
嗨2
嗨3
早上好!2
早上好!3
10.5 线程的同步机制与共享资源(自学)