分享
 
 
 

Java多线程学习笔记(二)

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Java多线程学习笔记(二)

作者:陶冶(无邪)

时间:2003.6.21

四、Java的等待通知机制

在有些时候,我们需要在几个或多个线程中按照一定的秩序来共享一定的资源。例如生产者--消费者的关系,在这一对关系中实际情况总是先有生产者生产了产品后,消费者才有可能消费;又如在父--子关系中,总是先有父亲,然后才能有儿子。然而在没有引入等待通知机制前,我们得到的情况却常常是错误的。这里我引入《用线程获得强大的功能》一文中的生产者--消费者的例子:

/* ==================================================================================

* 文件:ThreadDemo07.java

* 描述:生产者--消费者

* 注:其中的一些注释是我根据自己的理解加注的

* ==================================================================================

*/

// 共享的数据对象

class ShareData{

private char c;

public void setShareChar(char c){

this.c = c;

}

public char getShareChar(){

return this.c;

}

}

// 生产者线程

class Producer extends Thread{

private ShareData s;

Producer(ShareData s){

this.s = s;

}

public void run(){

for (char ch = 'A'; ch <= 'Z'; ch++){

try{

Thread.sleep((int)Math.random() * 4000);

}catch(InterruptedException e){}

// 生产

s.setShareChar(ch);

System.out.println(ch + " producer by producer.");

}

}

}

// 消费者线程

class Consumer extends Thread{

private ShareData s;

Consumer(ShareData s){

this.s = s;

}

public void run(){

char ch;

do{

try{

Thread.sleep((int)Math.random() * 4000);

}catch(InterruptedException e){}

// 消费

ch = s.getShareChar();

System.out.println(ch + " consumer by consumer.");

}while(ch != 'Z');

}

}

class Test{

public static void main(String argv[]){

ShareDatas = new ShareData();

new Consumer(s).start();

new Producer(s).start();

}

}

在以上的程序中,模拟了生产者和消费者的关系,生产者在一个循环中不断生产了从A-Z的共享数据,而消费者则不断地消费生产者生产的A-Z的共享数据。我们开始已经说过,在这一对关系中,必须先有生产者生产,才能有消费者消费。但如果运行我们上面这个程序,结果却出现了在生产者没有生产之前,消费都就已经开始消费了或者是生产者生产了却未能被消费者消费这种反常现象。为了解决这一问题,引入了等待通知(wait/notify)机制如下:

1、在生产者没有生产之前,通知消费者等待;在生产者生产之后,马上通知消费者消费。

2、在消费者消费了之后,通知生产者已经消费完,需要生产。

下面修改以上的例子(源自《用线程获得强大的功能》一文):

/* ==================================================================================

* 文件:ThreadDemo08.java

* 描述:生产者--消费者

* 注:其中的一些注释是我根据自己的理解加注的

* ==================================================================================

*/

class ShareData{

private char c;

// 通知变量

private boolean writeable = true;

// -------------------------------------------------------------------------

// 需要注意的是:在调用wait()方法时,需要把它放到一个同步段里,否则将会出现

// "java.lang.IllegalMonitorStateException: current thread not owner"的异常。

// -------------------------------------------------------------------------

public synchronized void setShareChar(char c){

if (!writeable){

try{

// 未消费等待

wait();

}catch(InterruptedException e){}

}

this.c = c;

// 标记已经生产

writeable = false;

// 通知消费者已经生产,可以消费

notify();

}

public synchronized char getShareChar(){

if (writeable){

try{

// 未生产等待

wait();

}catch(InterruptedException e){}

}

// 标记已经消费

writeable = true;

// 通知需要生产

notify();

return this.c;

}

}

// 生产者线程

class Producer extends Thread{

private ShareData s;

Producer(ShareData s){

this.s = s;

}

public void run(){

for (char ch = 'A'; ch <= 'Z'; ch++){

try{

Thread.sleep((int)Math.random() * 400);

}catch(InterruptedException e){}

s.setShareChar(ch);

System.out.println(ch + " producer by producer.");

}

}

}

// 消费者线程

class Consumer extends Thread{

private ShareData s;

Consumer(ShareData s){

this.s = s;

}

public void run(){

char ch;

do{

try{

Thread.sleep((int)Math.random() * 400);

}catch(InterruptedException e){}

ch = s.getShareChar();

System.out.println(ch + " consumer by consumer.**");

}while (ch != 'Z');

}

}

class Test{

public static void main(String argv[]){

ShareDatas = new ShareData();

new Consumer(s).start();

new Producer(s).start();

}

}

在以上程序中,设置了一个通知变量,每次在生产者生产和消费者消费之前,都测试通知变量,检查是否可以生产或消费。最开始设置通知变量为true,表示还未生产,在这时候,消费者需要消费,于时修改了通知变量,调用notify()发出通知。这时由于生产者得到通知,生产出第一个产品,修改通知变量,向消费者发出通知。这时如果生产者想要继续生产,但因为检测到通知变量为false,得知消费者还没有生产,所以调用wait()进入等待状态。因此,最后的结果,是生产者每生产一个,就通知消费者消费一个;消费者每消费一个,就通知生产者生产一个,所以不会出现未生产就消费或生产过剩的情况。

五、线程的中断

在很多时候,我们需要在一个线程中调控另一个线程,这时我们就要用到线程的中断。用最简单的话也许可以说它就相当于播放机中的暂停一样,当第一次按下暂停时,播放器停止播放,再一次按下暂停时,继续从刚才暂停的地方开始重新播放。而在Java中,这个暂停按钮就是Interrupt()方法。在第一次调用interrupt()方法时,线程中断;当再一次调用interrupt()方法时,线程继续运行直到终止。这里依然引用《用线程获得强大功能》一文中的程序片断,但为了更方便看到中断的过程,我在原程序的基础上作了些改进,程序如下:

/* ===================================================================================

* 文件:ThreadDemo09.java

* 描述:线程的中断

* ===================================================================================

*/

class ThreadA extends Thread{

private Thread thdOther;

ThreadA(Thread thdOther){

this.thdOther = thdOther;

}

public void run(){

System.out.println(getName() + " 运行...");

int sleepTime = (int)(Math.random() * 10000);

System.out.println(getName() + " 睡眠 " + sleepTime

+ " 毫秒。");

try{

Thread.sleep(sleepTime);

}catch(InterruptedException e){}

System.out.println(getName() + " 觉醒,即将中断线程 B。");

// 中断线程B,线程B暂停运行

thdOther.interrupt();

}

}

class ThreadB extends Thread{

int count = 0;

public void run(){

System.out.println(getName() + " 运行...");

while (!this.isInterrupted()){

System.out.println(getName() + " 运行中 " + count++);

try{

Thread.sleep(10);

}catch(InterruptedException e){

int sleepTime = (int)(Math.random() * 10000);

System.out.println(getName() + " 睡眠" + sleepTime

+ " 毫秒。觉醒后立即运行直到终止。");

try{

Thread.sleep(sleepTime);

}catch(InterruptedException m){}

System.out.println(getName() + " 已经觉醒,运行终止...");

// 重新设置标记,继续运行

this.interrupt();

}

}

System.out.println(getName() + " 终止。");

}

}

class Test{

public static void main(String argv[]){

ThreadB thdb = new ThreadB();

thdb.setName("ThreadB");

ThreadA thda = new ThreadA(thdb);

thda.setName("ThreadA");

thdb.start();

thda.start();

}

}

运行以上程序,你可以清楚地看到中断的过程。首先线程B开始运行,接着运行线程A,在线程A睡眠一段时间觉醒后,调用interrupt()方法中断线程B,此是可能B正在睡眠,觉醒后掏出一个InterruptedException异常,执行其中的语句,为了更清楚地看到线程的中断恢复,我在InterruptedException异常后增加了一次睡眠,当睡眠结束后,线程B调用自身的interrupt()方法恢复中断,这时测试isInterrupt()返回true,线程退出。

更多的线程组及相关的更详细的信息,请参考《用线程获得强大功能》一文及《Thinking in Java》。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有