Java线程通信

来源:互联网 发布:日本捕鲸 知乎 编辑:程序博客网 时间:2024/06/06 20:58
// 线程通信
// 不同的线程执行不同的任务,如果这些任务有某种联系,线程之间必须能够通信,协调完成工作,例如生产者和消费者共同操作堆栈
// 当堆栈为空时,消费者无法取出产品,应该通知生产者向堆栈中加入商品,当堆栈已满时,生产者无法继续加入产品,应该先通知消费者从堆栈中取出商品
// Object类中提供了两个用于线程通信的方法:
// wait()执行时释放对象的锁,java虚拟机把该线程放到该对象的等待池中,等待其他线程将它唤醒
// notify()执行该方法的线程唤醒在对象等待池中等待的一个线程,java虚拟机从对象的等待池中随机选择一个线程,把他转到对象的锁池中
// 假定t1和t2线程共同操作一个s对象,这两个线程可以通过s对象的wait()和notify()方法来进行通信
// 以下例子使得生产者线程和消费者线程能够互相通信
// 说明以下consumer2线程取产品时可能出现的流程:pop方法中先执行this.notifyAll()方法,此时this引用的stack1对象的等待池中没有任何线程,此时什么都不做
// 由于point=0时,执行this.wait()方法,consumer2线程释放stack1对象的锁,并且进入stack1对象的等待池,producer1线程获得stack1对象的锁,开始执行push()方法
// producer1线程首先执行this.notifyAll方法,此时在this引用的stack1对象的等待池中有一个consumer2线程,因此把这个线程转到stack1对象的锁池,producer1线程判断point
// 不为buffer.length-1,无需执行this.wait方法,producer1线程向堆中加入一个产品,然后退出push()方法,并且释放锁。在stack1对象的锁池中的consumer2线程获得了锁,转到
// 就绪状态,只要获的CPU,就能继续执行push()方法。
// 对于stack2对象,同时有两个生产者和一个消费者,流程和以上差不多的
// 当producer1,consumer1,consumer2线程执行this.notifyAll()方法时,只会唤醒this引用的stack1对象的等待池中的线程,而不会唤醒stack2对象的等待池中的线程,反之也是一样的
// 注意:wait()方法必须放在一个循环中,因为在多线程环境中,共享对象的状态随时可能被改变,当一个在对象等待池中的线程被唤醒后,并不一定立即恢复运行,等到这个线程获得锁及CPU才能继续运行
// 有可能此时对象的状态已经发生了变化,例如把wait()方法放在if语句中,wait方法放在一个循环中,那么当customer2恢复运行时,还会再次判断point的值是否为-1,如果是,再次执行wait()方法
// 如果wait在if语句中,那么当consumer2线程恢复运行时,不会再判断point的值是否为-1,因此会导致访问空的堆栈的错误。
// 此外,对一个对象的wait()和notify()的调用应该放在同步代码块中,并且同步代码块采用这个对象的锁,如果违反这个规则,尽管在编译时不会检查这种错误,但是运行时会抛出异常
public class Test13 {public static void main(String[] args) {Stack1 stack1 = new Stack1("stack1"); // 创建一个类对象Producer1 producer1 = new Producer1(stack1, "producer1"); // 创建一个操作这个对象属性的生产者Consumer1 consumer1 = new Consumer1(stack1, "consumer1"); // 创建一个操作这个对象属性的生产者Consumer1 consumer2 = new Consumer1(stack1, "consumer2"); // 创建一个操作这个对象属性的消费者Stack1 stack2 = new Stack1("stack2"); // 创建一个类对象Producer1 producer2 = new Producer1(stack2, "producer2"); // 创建一个操作这个对象属性的生产者Producer1 producer3 = new Producer1(stack2, "producer3"); // 创建一个操作这个对象属性的生产者Consumer1 consumer3 = new Consumer1(stack2, "consumer3"); // 创建一个操作这个对象属性的消费者}}class Producer1 extends Thread {private Stack1 theStack;public Producer1(Stack1 s, String name) {super(name); // 调用线程名称theStack = s; // 赋给同一对象start(); // 启动线程}public void run() {String goods;for (int i = 0; i < 200; i++) {synchronized (theStack) { // 对同一对象进行锁定操作,不加这个同步代码,会发生两个线程都执行了?+1,因为没有加锁,所以结果一样了,这是不对的goods = "goods" + (theStack.getPoint() + 1);theStack.push(goods); // 对theStack这个对象的point执行+1操作System.out.println(getName() + ": push " + goods + " to "+ theStack.getName());}yield(); // 将CPU使用权让给运行级别一样的线程}}}class Consumer1 extends Thread {private Stack1 theStack;public Consumer1(Stack1 s, String name) {super(name); // 调用线程名称theStack = s; // 赋给同一对象start(); // 启动线程}public void run() {String goods;for (int i = 0; i < 200; i++) {synchronized (theStack) { // 对同一对象进行锁定操作goods = theStack.pop(); // 对theStack这个对象的point执行-1操作System.out.println(getName() + ": pop " + goods + " from "+ theStack.getName());}yield(); // 将CPU使用权让给运行级别一样的线程}}}class Stack1 {private String name;private String[] buffer = new String[1000];int point = -1;public Stack1(String name) {this.name = name;}public String getName() {return name;}public synchronized int getPoint() {return point;}// 消费public synchronized String pop() {this.notifyAll();while (point == 0) {System.out.println(Thread.currentThread().getName() + ": wait");try {this.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}String goods = buffer[point];buffer[point] = null;Thread.yield();if (point > 0) {point--;}return goods;}// 生产public synchronized void push(String goods) {this.notifyAll();while (point == buffer.length - 1) {System.out.println(Thread.currentThread().getName() + ": wait");try {this.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}point++;Thread.yield();buffer[point] = goods;}}

0 0
原创粉丝点击