文章目录
1 问题描述
开启两个线程,一个线程产生1到10的随机数,另一个线程输出。
2 传统的线程通信
2.1 说明
传统的线程通信是使用Object类的wait(),notify(),notifyAll() 方法。这三个方法都由同步监视器调用。可分为下面两种情况。
- 对于synchronized修饰的同步方法,同步监视器就是this.
- 对于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();
}
}
}