Java线程通信

1 问题描述

开启两个线程,一个线程产生1到10的随机数,另一个线程输出。

2 传统的线程通信

2.1 说明

传统的线程通信是使用Object类的wait(),notify(),notifyAll() 方法。这三个方法都由同步监视器调用。可分为下面两种情况。

  1. 对于synchronized修饰的同步方法,同步监视器就是this.
  2. 对于synchronized修饰的同步代码块,同步监视器就是synchronized后括号包含的对象。

2.2 实现代码

主函数

public class TraditionCommunication {
  public static void main(String argc[]){
  	Message m=new Message();
  	ProductThread product=new ProductThread();
  	product.setR(m);
  	PrintThread print=new PrintThread();
  	print.setR(m);
  	new Thread(product).start();
  	new Thread(print).start();
  }
}

临界资源

public class Message {
  // 用于产生随机数
  private static Random r = new Random();
  //用于存储产生的随机数
  public static int i;
  //信号量,用于控制线程
  private boolean isAllowPrint = false;

  public synchronized int product() {
  	if (isAllowPrint) {// 不允许生成我就等待
  		try {
  			wait();//线程等待
  		} catch (InterruptedException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}

  	}
  	i = r.nextInt(10);
  	System.out.println("生产线程产生了一个随机数" + i);
  	isAllowPrint = true;
  	notify();
  	return i;
  }

  public synchronized int getI() {
  	if (!isAllowPrint) {// 不允许打印我就等待
  		try {
  			wait();//线程等待
  		} catch (InterruptedException e) {
  			// TODO Auto-generated catch block
  			e.printStackTrace();
  		}
  	}
  	System.out.println("打印线程打印出刚生成的随机数" + i);
  	isAllowPrint = false;
  	notify();
  	return i;
  }
}

产生随机数线程

public class ProductThread implements Runnable {
  // r相当于临界资源
  private Message r;

  public Message getR() {
  	return r;
  }

  public void setR(Message r) {
  	this.r = r;
  }

  /**
   * TODO 简单描述该方法的实现功能(可选).
   * 
   * @see java.lang.Runnable#run()
   */
  public void run() {
  	// 生成5个随机数
  	for (int i = 1; i <= 5; ++i) {
  		r.product();
  	}
  }

}

打印随机数线程

public class PrintThread implements Runnable {
	// r相当于临界资源
	private Message r;

	public Message getR() {
		return r;
	}

	public void setR(Message r) {
		this.r = r;
	}

	/**
	 * TODO 简单描述该方法的实现功能(可选).
	 * 
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		// 读取5次
		for (int i = 1; i <= 5; ++i) {
			r.getI();
		}
	}

}

3 使用Condition控制线程通信

3.1 说明

其实使用Condition和传统的线程通信相似,只不过使用Lock对象保持同步时,没有了隐式的同步监视器,所以就把wait(),notify(),notifyAll() 改成了其他方法。
Condition类提供了下面三个类似于wait(),notify(),notifyAll()的方法:

  • await():导致当前线程 等待 ,直到其他线程调用该Condition的 signal(),signalAll() 方法时唤醒该线程
  • signal(): 唤醒在此Lock对象上等待的单个线程。如果多个线程在此Lock对象上等待,则唤醒哪一个线程是随机的
  • signalAll(): 唤醒所有在此Lock对象上等待的线程。

3.2 代码实现

实现只用修改Message.java里面的几行代码就行了

Message.java

public class Message {
  // 可重入锁控制同步
  private final Lock lock = new ReentrantLock();
  // 将condition和Lock绑定
  private final Condition condition = lock.newCondition();
  // 用于产生随机数
  private static Random r = new Random();
  // 用于存储产生的随机数
  public static int i;
  // 信号量,用于控制线程
  private boolean isAllowPrint = false;

  public int product() {
  	// 加锁
  	lock.lock();
  	try {
  		if (isAllowPrint) {// 不允许生成我就等待
  			try {
  				condition.await();
  			} catch (InterruptedException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		}
  		i = r.nextInt(10);
  		System.out.println("生产线程产生了一个随机数" + i);
  		isAllowPrint = true;
  		condition.signal();
  		return i;
  	} finally {
  		// 释放锁
  		lock.unlock();
  	}
  }

  public int getI() {
  	// 加锁
  	lock.lock();
  	try {
  		if (!isAllowPrint) {// 不允许打印我就等待
  			try {
  				condition.await();// 线程等待
  			} catch (InterruptedException e) {
  				// TODO Auto-generated catch block
  				e.printStackTrace();
  			}
  		}
  		System.out.println("打印线程打印出刚生成的随机数" + i);
  		isAllowPrint = false;
  		condition.signal();
  		return i;
  	} finally {
  		lock.unlock();
  	}
  	// 释放锁
  }
}

4 使用阻塞队列(BlockingQueue)控制线程通信

4.1 说明

BlockingQueue主要用途不是容器,而是作为线程同步工具

他有一个特征:当生产者线程试图向队列中放入元素时,若队列满,则阻塞线程;当消费者线程试图取队列中的元素时,若队列空,则线程阻塞。

BlockingQueue提供了两个支持阻塞的方法:

  • put(E e):尝试把e放入到BlockingQueue中,若该队列满,则阻塞该线程。
  • take():尝试从队列头部取出元素,若队列为空,则阻塞该线程

当然,BlockingQueue是继承了Queue队列,也可以使用Queue接口的方法。这些方法归纳如下:

  • 在队列尾部插入元素。有add(E e)offer(E e)put(E e),当队列满时,这三个方法分别会抛出异常、返回false、阻塞队列。
  • 在队列头部删除元素并返回删除元素。有remove()poll()take((),当队列为空时,这三个方法分别抛出异常、返回false、阻塞队列。
  • 在队列头部取出但不删除元素。有element()peek(),当队列为空时,这两个方法分别抛出异常、放回false 。

4.2 代码实现

同样我们只用修改message.java

public class Message {
	// 用于产生随机数
	private static Random r = new Random();
	//用于存储产生的随机数
	public static BlockingQueue<Integer> i=new SynchronousQueue<Integer>();

	public  void product() {
		try {
			//模拟生产(需要时间)
			Thread.sleep(1000);
			i.put(r.nextInt(10));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("生产线程产生了一个随机数" + i);
	}

	public void getI() {
		try {
			Thread.sleep(1000);
			int temp=i.take();
			System.out.println("打印线程打印出刚生成的随机数" + temp);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值