Java多线程(3)
来源:互联网 发布:淘宝订单评价有效期 编辑:程序博客网 时间:2024/06/05 20:19
七、线程间通信
线程间通信,主要介绍问题的引出和如何解决等内容。
1、问题的引出
实例:
把一个数据存储空间划分为两个部分:一部分用于存储人的姓名,另一部分用于存储人的性别。这里包含两个线程:一个线程向数据存储空间添加数据(producer),另一个线程从数据存储空间去除数据(consumer)。这个应用会出现两种意外。
第一种意外:假设producer刚向数据存储空间添加了一个人的姓名,还没有加入这个人的性别,cpu就切换到consumer线程,consumer则把这个人的姓名和上一个人的性别联系到了一起。
第二种意外:producer放入了若干次数据,consumer才开始取数据,或者是,consumer 取完一个数据后,还没等到producer放入新的数据,又重复取出已去过的数据。
2、问题如何解决
程序中的生产者线程和消费者线程运行的是不同的程序代码,因此编写包含run方法的两个类来完成这两个线程,一个是生产线程Producer,另一个是消费者线程Consumer。
class Producer implements Runnable {
public void run(){
//数据空间存放数据
}
}
class Consumer implements Runnable{
public void run(){
//从数据空间读取数据
}
}
还需要定义一个数据结构来存储数据
class P{
String name;
String sex;
}
范例1:
class P{
String name = "LiSi";
String sex = "Girl";
}
class Producer implements Runnable {
P q = null;
public Producer(P q){
this.q = q;
}
public void run(){
int i = 0;
while(true){
if(i==0){
q.name = "ZhangSan";
q.sex = "Boy";
}else{
q.name = "Lisi";
q.sex = "Girl";
}
i = (i+1)%2;
}
}
}
class Consumer implements Runnable{
P q = null;
public Consumer (P q){
this.q = q;
}
public void run(){
while(true){
System.out.println(q.name+"----->"+q.sex);
}
}
}
public class Test {
public static void main(String args[]){
P q = new P();
Thread pro = new Thread(new Producer(q));
Thread con = new Thread(new Consumer(q));
pro.start();
con.start();
}
}
运行结果片段:
......
Lisi----->Girl
ZhangSan----->Boy
ZhangSan----->Boy
Lisi----->Girl
Lisi----->Boy
ZhangSan----->Girl
ZhangSan----->Boy
ZhangSan----->Girl
ZhangSan----->Boy
Lisi----->Boy
Lisi----->Boy
ZhangSan----->Boy
ZhangSan----->Girl
......
从运行结果来看,打印出现了混乱的情况,这是什么原因?在程序中,Producer类和Consumer类都操纵了同一个P类,这就是有可能出现Producer类还未操纵玩P类,Consumer类就已经将P类中的内容取走了,这就是资源不同步的原因。为此,可在P类中增加两个同步方法:set()和get()。
范例2:
class P{
String name = "LiSi";
String sex = "Girl";
public synchronized void set(String name,String sex){
this.name = name;
this.sex = sex;
}
public synchronized void get(){
System.out.println(this.name+"----->"+this.sex);
}
}
class Producer implements Runnable {
P q = null;
public Producer(P q){
this.q = q;
}
public void run(){
int i = 0;
while(true){
if(i==0){
q.set("ZhangSan","BOy");
}else{
q.set("LiSi","Girl");
}
i = (i+1)%2;
}
}
}
class Consumer implements Runnable{
P q = null;
public Consumer (P q){
this.q = q;
}
public void run(){
while(true){
q.get();
}
}
}
public class Test {
public static void main(String args[]){
P q = new P();
Thread pro = new Thread(new Producer(q));
Thread con = new Thread(new Consumer(q));
pro.start();
con.start();
}
}
运行结果:
........
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
LiSi----->Girl
.......
这个输出结果顺序上没有任何问题,但又有新的问题产生。Consumer线程对Producer线程放入的一次数据连续的读取了多次,这并不符合实际的要求。实际要求的结果是,Producer方一次数据,Consumer就取一次;反之,Producher也必须等到Consumer取完后才能放入新的数据,而这一问题的解决就需要使用下面讲到的线程间的通信。
Java是通过Object类的wait、notify、notifyAll这几个方法来实现线程间的通信的,又因为所有的类都是从Object继承的,所以任何类都可以直接使用这些方法。
wait:告诉当前线程放弃监视器并进入睡眠状态,直到其他线程进入同一监视器并调用notify为止;
nofity:唤醒同一对象监视器中调用wait的第一个线程。这类似于排队买票,一个人买完之后,后面的人才可以继续买;
notifyAll:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行。
我们现在将P类修改如下:
class P{
String name = "LiSi";
String sex = "Girl";
boolean bFull = false;
public synchronized void set(String name,String sex){
if(bFull){
try{
wait();
}catch(InterruptedException e){}
}
this.name = name;
try{
Thread.sleep(10);
}catch(Exception e){}
this.sex = sex;
bFull = true;
notify();
}
public synchronized void get(){
if(!bFull){
try{
wait();
}catch(InterruptedException e){}
}
System.out.println(this.name+"----->"+this.sex);
bFull = false;
notify();
}
}
当Consumer线程取走数据后,bFull值为false,当Producer线程放入数据后,bFull值为true。只有bFull为true时,Consumer线程才能取走数据,否则就必须等待Producer线程放入新的数据后的通知;反之,只有bFull为false,Producer线程才能放入新的数据,否则就必须等待Consumer线程取走数据后的通知。
运行结果如下:
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
ZhangSan----->BOy
LiSi----->Girl
wait、notify、notifyAll这3个方法只能在synchronized方法中调用,即无论线程调用一个对象的wait还是notify方法,该线程必须先得到该对象的锁标记。这样,notify就只能唤醒同一对象监视器中调用wait的线程。而使用多个对象监视器,就可以分别有多个wait、notify的情况,同组里的wait只能被同组的notify唤醒。
0 0
- java多线程 [3]
- Java多线程(3)
- JAVA进阶-多线程(3)
- java基础3:多线程
- Java基础--多线程3
- java多线程3--synchronized
- #java读书笔记#多线程3
- java多线程 -- 总结3
- Java多线程基础(3)
- java多线程3
- Java ---多线程3
- Java多线程3 --- 多线程处理文件压
- Java多线程系列-多线程合集3
- Java多线程系列(3)
- java多线程学习笔记3
- java多线程设计模式 (3)
- Java多线程的通信3
- java多线程(3)-- join( )
- 一个好用的MATLAB工具包VLFeat
- poj3628 Bookshelf 2 DP 01背包
- JAVA 网络长短连接
- 开始python学习
- ANE接入平台心得记录(安卓)
- Java多线程(3)
- C++顺序栈的实现
- 修改JFileChooser对话框风格,设置打开对话框的默认文件名,获取改变路径之后的文件保存路径
- SQL基础-->层次化查询(START BY ... CONNECT BY PRIOR)
- SparkStreaming VS 单机
- Linux环境编程之文件I/O(七):目录文件及操作
- 游戏开发中的一些基本方法(转载)
- HDU 1716 排列2 字典序
- ANE原生代码的调试(安卓)