`
晓伟哥
  • 浏览: 6233 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java多线程入门二

阅读更多
      上节,我们谈到了生产者/消费者模型,这里,我不得不把我的一次实验结果粘贴出来,本来第一次运行的挺好的,可是第二次竟然出现了如下结果:
Producer:p  produced:1
Consumer:c  consumed:1
Consumer:c  consumed:2
Producer:p  produced:2
Producer:p  produced:3
Consumer:c  consumed:3
Producer:p  produced:4
Consumer:c  consumed:4
Producer:p  produced:5
Consumer:c  consumed:5
      奇怪,怎么第三行是消费,第四行才是生产者,我开始是怀疑我手一哆嗦点错了,可是,在经过数次运行之后,仍然出现了消费在前,生产在后......
      嘿嘿,其实这是假象,我们看到的并不是先消费再生产,其实确实是先生产再消费,只是控制台先打印出了消费语句再打印出了生产语句.为什么呢?这是因为生产者线程生产数据(put())之后未打印生产语句之前突然由于某种原因(如时间分片完毕)发生中断,导致没及时打印生产语句,这时,消费者夺得了cpu资源,取走数据并打印了消费语句,随后当cpu重新处理生产者线程时,回到刚才生产者线程的中断现场,执行完刚没执行的生产语句.这就造成了先消费后生产的假象.可以想象,这样的一个视觉性漏洞,是会给实际应用带来重大损失的.
      那么,如何改变这一弊端呢?第一种思路就是我们可以将打印语句写在同步方法中,即把生产语句写在put()中,消费语句写在get()中,这样,如果线程进入到了put()中,即使这时突然中断,生产者线程也不会交出对象锁,消费者线程也不会执行get()了,更不会在生产语句之前打印get()中的消费语句了.第二种思路就是我们不让生产方法(put())和消费方法(get())单独同步,而是让生产方法和生产语句一起和消费方法和消费语句一起同步,互相排斥.
      上述方法在解决某些实际问题时需要三思后行,站在理解思想挖掘大脑潜力的基础上选取最佳方法,需要我们灵活运用.
      这里我采取第二种方法.

      我们先定义要处理的资源(Box):
public class Box {
private int value;// 要进行读写的值

private boolean available = false;// 开关变量,true即表示value已生产,false即表示value已消费

public int get() {
while (available == false) {// Box无数据
try {
wait();// 交出对象锁,等待生产者写入数据
} catch (InterruptedException e) {
e.printStackTrace();
}
}
available = false;// 开关变量取反,示意消费者可以取走数据
notifyAll();// 唤醒线程池中所有阻塞线程
return value;
}

public void put(int value) {
while (available == true) {// Box有数据
try {
wait();// 交出对象锁,等待消费者取走数据
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.value = value;
available = true;// 开关变量取反,示意生产者可以生产数据
notifyAll();// 唤醒线程池中所有阻塞线程
}
}


      线程--生产者:

public class Producer extends Thread {
private Box box;// 要处理的资源

private String name;// 生产者名称

// 构造器
public Producer(Box box, String name) {
this.box = box;
this.name = name;
}

public void run() {
synchronized (box) {
for (int i = 1; i < 6; i++) {
box.put(i);// 写入数据i(即给box中的value设定初值)
System.out.println("Producer:" + name + "  " + "produced:" + i);// 打印
try {
sleep((int) (Math.random() * 100));// 强制睡眠,用以等待消费者取走数据
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

}


      线程--消费者:

public class Consumer extends Thread {
private Box box;// 要处理的资源

private String name;// 消费者名称

// 构造器
public Consumer(Box box, String name) {
this.box = box;
this.name = name;
}

public void run() {
synchronized (box) {
int value = 0;
for (int i = 1; i < 6; i++) {
value = box.get();// 取走数据(即获取box的value值)
System.out.println("Consumer:" + name + "  " + "consumed:"
+ value);
try {
sleep((int) (Math.random() * 100));// 强制睡眠,用以等待生产者生产数据
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}


      测试类:

package produceConsumer;

public class ProducerConsumerTest {
public static void main(String[] args) {
Box box = new Box();
new Producer(box, "p").start();
new Consumer(box, "c").start();
}
}

      运行结果:
Producer:p  produced:1
Consumer:c  consumed:1
Producer:p  produced:2
Consumer:c  consumed:2
Producer:p  produced:3
Consumer:c  consumed:3
Producer:p  produced:4
Consumer:c  consumed:4
Producer:p  produced:5
Consumer:c  consumed:5

      读者朋友可以尝试着,采用继承Runnable的方式去实现java多线程入门一中的生产者/消费者模型.

下接:java多线程入门三http://172672421-qq-com.iteye.com/admin/blogs/1129791
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics