第 7章 多线程北京大学计算机系代亚非
2第 7章 多线程
7.1 多线程基本概念
7.2 创建线程的方式
7.3 线程的挂起与唤醒
7.4 多线程问题
7.5 小结
37.1 多线程基本概念文件 输入输出装置各种系统资源数据区段程序区段只有一个地方在执行文件 输入输出装置各种系统资源数据区段程序区段同时有数个地方在执行传统的进程 多线程的任务
47.1 多线程基本概念
多线程的优势,
减轻编写交互频繁、涉及面多的程序的困难,
程序的吞吐量会得到改善,
由多个处理器的系统,可以并发运行不同的线程,(否则,任何时刻只有一个线程在运行 )
57.1 多线程基本概念
线程与进程的区别,
多个进程的内部数据和状态都是完全独立的,
而多线程是共享一块内存空间和一组系统资源,有可能互相影响,
线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。
67.1 多线程基本概念
对线程的综合支持是 Java技术的一个重要特色,它提供了 thread类、监视器和条件变量的技术,
虽然 Macintosh,Windows NT,Windows 9等操作系统支持多线程,但若要用 C或 C++编写多线程程序是十分困难的,因为它们对数据同步的支持不充分,
77.2 创建线程的方式
1,public class mythread extends Applet
implements Runnable
(小应用或已经是某个类的子类时 )
2,继承类 Thread
public class mythread extends Thread
3,上述两种方法中都可用类 Thread产生线程的对象 Thread newthread;
4,创建并启动线程
newthread=new Thread(this);
newthread.start();
87.2 创建线程的方式
5,run方法是运行线程的主体,启动线程时,由
java直接调用 public void run()
6.停止线程,由小应用程序的 stop调用线程的
stop newthread.stop()
7 sleep方法的作用,暂停线程的执行,让其它线程得到机会,sleep要丢出异常,必须抓住,
Try{sleep(100)}catch(InterruptedException e){}
例,小应用程序中不用 Runnable接口仍然可以使用线程 (不调用主类的方法和调用主类的方法 )
9
import java.applet.*;
public class thread extends Applet
{ mythread t1=new mythread();
public init()
{ t1.start();}
class mythread extends Thread
{ public void run()
{ for (int i=0;i<4;i++) System.out.println(,”+i);
{ try{sleep(400);
} catch(InteruptedException e){ }}
}
7.2 创建线程的方式
107.2 创建线程的方式public class mainclass extends Applet
{ C t1=new C(this);
public void init() { t1.start();}
public void paint(Graphics g)
{ g.drawString("Hello,java",10,50);}}
class C extends Thread
{ mainclass a;
C(mainclass b)
{ a=b; }
public void run()
{ while(true){ a.repaint();
try{sleep(400);}
catch(InterruptedException e){} }}
117.2 创建线程的方式
8.其它常用的方法
isAlive,判断线程目前是否正在执行状态中
if(newthread.isAlive()) newthread.stop();
resume:要求被暂停得线程继续执行
suspend:暂停线程的执行
join:等待线程执行完毕
thatThread.join();被等待的那个线程不结束,当前线程就一直等待,
yield:将执行的权力交给其它线程,自己到队列的最后等待,
127.2 创建线程的方式
9.线程的优先权
某一时刻只有一个线程在执行,调度策略为固定优先级调度,
newthread.setPriority(Thread.MIN_PRIORITY)
级别有,MIN-PRIORITY
NOM_PRIORITY
MAX-PRIORITY
10,自私的线程,有很高的优先权的线程,不主动睡眠或让出处理器控制权,
137.2 创建线程的方式
new Thread()
New Thread Runnable
start() Not Runnable
stop() stop()
Dead
yield()
stop() or
run()exit
.,
suspend()
sleep()
wait()
resume()
.
11,线程的状态
147.2 创建线程的方式
当一个线程执行完所有语句后就自动终止,调用线程的 stop()方法,也可以强制终止线程。
如果希望线程正常终止,可采用标记来使线程中的 run()方法退出。
157.2 创建线程的方式
public class Xyz implements Runnable
{ private boolean timeToQuit=false;
public void run()
{ while (! timeToQuit)
{…..}
//clean up before run() ends;
}
public void stopRunning()
{ timeToQuit=true;}
}
167.2 创建线程的方式
public class ControlThread
{ private Runnable r=new Xyz();
private Thread t=new Thread(r);
public void startThread()
{ t.start(); }
publi void stopThread()
{ r.stopRunning();}
}
17
7.3 线程的挂起与唤醒
暂停线程的执行等待条件满足再执行,
下面的例子显示线程的挂起和唤醒
小应用程序第一次开始时,线程被启动
浏览器改变页面时,小应用程序的 stop()方法被调用,线程被挂起,
浏览器回到原来的页面时,线程被唤醒,
187.3 线程的挂起与唤醒
public void start()
{ if (mythread==null)
{mythread=new Thread(); mythread.start();}
else { mythread.resume();}
}
public void run()
{ while(true){ try{sleep(100);}
catch(InterruptedException e) {}}
}
public void stop()
{ mythread.suspend(); }.
197.4 多线程问题 ---执行的顺序
多个线程运行时,调度策略为固定优先级调度,级别相同时,由操作系统按时间片来分配
下面给出的例子中,共运行三个线程,它们做同样的事,每次打印循环次数和自己的序列号,运行结果表明,它们并不是连续运行的,
在上例中如果给某个线程赋予较高的优先权,
则发现这个进程垄断控制权
thread.setPriority(Thread.MAX_PRIORITY)
thread\multithread.class--f1.bat
thread\Priority.class---f2.bat
207.3 多线程问题//多个进程运行时执行顺序是交叉的
class multithread extends Thread
{ int threadNum;
public static void main(String args[])
{ multithread array[]=new multithread[3];
for (int i=0;i<3;i++) array[i]=new multithread(i);
for (int i=0;i<3;i++) array[i].start(); }
multithread(int SerialNum)
{ super(); threadNum=SerialNum; }
public void run()
{ for(int j=0;j<5;j++)
System.out.println(“<"+j+"> +MySerialNum);
System.out.println("thread "+threadNum+ "bye.");}}
217.4 多线程问题 ---如何写多线程
1.分别定义不同的线程类,在各自的 run方法中定义线程的工作
class mythread1 extends Thread
{ public void run{….} }
class mythread2 extends Thread
{ public void run{….} }
2,在主类中实例化各线程类,并启动线程,
public class demo extends Applet
{ public void init()
{ mythread t1=new mythread1();
mythread t2=new mythread2();
t1.start(); t2.start();} }
227.4 多线程问题 ---如何写多线程
练习,将窗口分为上下两个区,分别运行两个线程,一个在上面的区域中显示由右向左游动的字符串,另一个在下面的区域从左向右游动的字符串,
方法一,一个线程,在 paint方法中使用两个输出字符串的语句
public void paint(Graphics g)
{ if y1<0 y1=200 else y1=y1-10;
if y2>200 y2=0 else y2=y2+10;
g.drawString(“hello,Java!”,20,y1,);
g.drawString(“hello,Java!”,40,y2,);
}
237.4 多线程问题 ---如何写多线程
方法二,定义两个类,运行各自的线程,各自有自己的 paint()方法,
注意,两个小应用程序必须是 panel类或者是
canvas类,将小应用的区域分成两块,否则不能运行 paint语句,
247.4 多线程问题 ---线程间的通信
1,线程间的通信可以用管道流,.
创建管道流,
PipedInputStream pis=new PipedInputStream();
PipedOutputStream pos=new
PipedOutputStream(pis);
或,
PipedOutputStream pos=new
PipedOutputStream();
PipedInputStream pis=new
PipedInputStream(pos);
线程 1 PipedOutputStream PipedInputStream
输出流 outStream 输入流 inStream
线程 2
257.4 多线程问题 ---线程间的通信
管道 流不能直 接读写
PrintStream p = new PrintStream( pos );
p.println(“hello”);
DataInputStream d=new DataInputStream(pis);
d.readLine();
2,通过一个中间类来传递信息,
线程 2线程 1 中间类 m
s s
m.write(s) s=m.read()
write() read()
printStream DataInputStream
267.4 多线程问题 --线程间的通信
管道流可以连接两个线程间的通信
下面的例子里有两个线程在运行,一个往外输出信息,一个读入信息,
将一个写线程的输出通过管道流定义为读线程的输入,
outStream = new PipedOutputStream();
inStream = new PipedInputStream(outStream);
new Writer( outStream ).start();
new Reader( inStream ).start();
27
(thread\Pipethread.class--f3.bat)
7.4 多线程问题 --线程间的通信主类 Pipethread
辅类
Writer
线程类辅类
Reader
线程类管道流将数据写到输出流从流中读数据输入流作为参数传给 Writer
Writer( outStream )
287.4 多线程问题 --线程间的通信
,
public class Pipethread
{ public static void main(String args[])
{ Pipethread thisPipe = new Pipethread();
thisPipe.process(); }
public void process()
{ PipedInputStream inStream;
PipedOutputStream outStream;
PrintStream printOut;
try{ outStream = new PipedOutputStream();
inStream = new PipedInputStream(outStream);
new Writer( outStream ).start();
new Reader( inStream ).start();
}catch( IOException e ){ }
}}
297.4 多线程问题 ---线程间的通信class Reader extends Thread { private PipedInputStream inStream;//从中读数据
public Reader(PipedInputStream i)
{ inStream = i; }
public void run()
{ String line; DataInputStream d;
boolean reading = true;
try{ d = new DataInputStream( inStream );
while( reading && d != null){
try{line = d.readLine();
if( line != null ){
System.out.println(,Read," + line ); }
else reading = false;
}catch( IOException e){ } }
catch( IOException e ){ System.exit(0); }
try{ Thread.sleep( 4000 );}
catch( InterruptedException e ){}}}
307.4 多线程问题 --线程间的通信
,
class Writer extends Thread
{ private PipedOutputStream outStream;//将数据输出
private String messages[ ]= { "Monday","Tuesday ",
"Wednsday","Thursday","Friday,",
"Saturday:","Sunday,"};
public Writer(PipedOutputStream o)
{ outStream = o; }
public void run()
{ PrintStream p = new PrintStream( outStream );
for (int i = 0; i < messages.length; i++) {
p.println(messages[ i ]);
p.flush();
System.out.println("WrIte:" + messages[i] ); }
p.close(); p = null;
}}
317.3 多线程问题 ---资源协调
1,数据的完整性线程 1
线程 2
线程 10
资源
withdrwal() withdrwal()
透支余额变量
327.3 多线程问题 ---资源协调
对共享对象的访问必须同步,叫做条件变量,
Java语言允许通过监视器 (有的参考书称其为管程 )使用条件变量实现线程同步,
监视器阻止两个线程同时访问同一个条件变量,
它的如同锁一样作用在数据上,
线程 1进入 withdrawal方法时,获得监视器 (加锁 );当线程 1的方法执行完毕返回时,释放监视器 (开锁 ),线程 2的 withdrawal方能进入,
withdrawal()
线程 1
监视器 线程 2
337.3 多线程问题 ---资源协调
用 synchronized来标识的区域或方法即为监视器监视的部分。
一个类或一个对象由一个监视器,如果一个程序内有两个方法使用 synchronized标志,则他们在一个监视器管理之下,
一般情况下,只在方法的层次上使用关键区
read write
监视器线程 1 线程 2
347.3 多线程问题 ---资源协调此处给出的例子演示两个线程在同步限制下工作的情况,
class Account
{ statics int balance=1000; //为什么用 static?
statics int expense=0;
public synchronized void withdrawl(int amount)
{ if (amount<=balance)
{ balance-=amount;
expense+=amount;}
else
{ System.out.println(“bounced:,+amount);}
}}
357.3 多线程问题 ---资源协调
2,等待同步数据生产者 消费者
.
.
共享对象
write read
可能出现的问题,
生产者比消费者快时,消费者会漏掉一些数据没有取到
消费者比生产者快时,消费者取相同的数据,
notify()和 wait ()方法用来协调读取的关系,
notify()和 wait ()都只能从同步方法中的调用,
367.3 多线程问题 ---资源协调
notify的作用是唤醒正在等待同一个监视器的线程,
wait的作用是让当前线程等待
信息版例子
read()方法在读信息之前先等待,直到信息可读,读完后通知要写的线程,
write()方法在写信息之前先等待,直到信息被取走,写完后通知要读的进程,
DemoWait.class--->f4.bat
377.3 多线程问题 ---资源协调
writer reader
aaaa
bbbbb
cccc
aaaaaaaaaaaaaaaa aaaa aaaa
bbbbb
cccccccccccccccc
bbbbb
cccc
387.3 多线程问题 ---资源协调
class WaitNotifyDemo {
public static void main(String[] args) {
{ MessageBoard m = new MessageBoard();
Reader readfrom_m = new Reader(m);
Writer writeto_m=new Writer(m);
readfrom_m.start();
writeto_m.start();
}
}
397.3 多线程问题 ---资源协调class MessageBoard {
{ private String message;
private boolean ready = false;(信号灯 )
public synchronized String read()
{ while (ready == false)
{ try { wait(); } catch (InterruptedException e) { } }
ready = false;
notify(); //起始状态先写后读
return message;
}
public synchronized void write(String s)
{ while (ready == true)
{ try { wait(); } catch (InterruptedException e) { } }
message = s; ready = true; notify();
}}
407.3 多线程问题 ---资源协调class Reader extends Thread
{ private MessageBoard mBoard;
public Reader(MessageBoard m)
{ mBoard = m; }
public void run()
{ String s = " ";
boolean reading = true;
while( reading ){
s = mBoard.read();
System.out.println("Reader read," + s);
if( s.equals("logoff") ) reading = false; }
System.out.println("Finished,10 seconds...");
try{ sleep( 10000 ); }
catch (InterruptedException e) { } } }
417.3 多线程问题 ---资源协调class Writer extends Thread { private MessageBoard mBoard;
private String messages[ ]= {
"Monday,------------------------",
“…..”,
"Sunday,----------------------"};
public Writer(MessageBoard m)
{ mBoard = m; }
public void run() {
{ for (int i = 0; i < messages.length; i++) {
mBoard.write(messages[ i ]);
System.out.println("Writer wrote:" + messages[i] );
try { sleep((int)(Math.random() * 100));
} catch (InterruptedException e) { }
}
mBoard.write("logoff"); } }
427.3 多线程问题 ---资源协调
多线成问题 ---资源的协调和锁定
1,死锁问题如果你的持有一个锁并试图获取另一个锁时,就有死锁的危险,
解决死锁问题的方法,给条件变量施加排序线程 2
pen
线程 1
note
把,pen”给我,我才能给你,note”
把,note”给我,我才能给你,pen”
437.3 多线程问题 ---daemon线程
什么是 daemon(守护 )?
在客户 /服务器模式下,服务器的作用是等待用户发来请求,并按请求完成客户的工作
守护线程是为其它线程提供服务的线程
守护线程一般应该是一个独立的线程,它的
run()方法是一个无限循环,
守护线程与其它线程的区别是,如果守护线程是唯一运行着的线程,程序会自动退出客户端 服务器端request
daemon
447.4 小结
1,实现线程有两种方法,
实现 Ruannable接口
继承 Thread类
2,在小应用中通常在 start中创建线程
3.当新线程被启动时,java调用该线程的 run方法,它是 Thread的核心,
4,线程由四个状态,新生,运行,暂停,死亡
5,线程间的通信方式由三种,完全共享数据,通过监视器,通过 join.
457.4 小结
6,两个或多个线程竞争资源时,需要用同步的方法协调资源,
7,多个线程执行时,要用到同步方法,即使用
synchronized的关键字设定同步区
8,wait和 notify起协调作用
9,守护进程的特点是当程序中制胜它自己时,会自动中止,
46作业
创建两个线程的实例,分别将一个数组从小大大和从达到小排列,输出结果,