您的位置:首页 > 编程语言 > Java开发

java基础——多线程之间通信

2015-03-25 00:04 127 查看
多线程之间的通信
1、 定义一个生产者和一个消费者,每当生产者生产出一个产品时,消费者就把他消费掉,也就是说,消费者和生产者都在操作这同一个资源,只是操作的方式不同一个是生产,一个是消费,所以我们可以定义三个类,一个是存放资源的,一个是生产者的还有一个是消费者的,生产者和消费者都在操作着存放资源的这个类那么怎么使这两个类可以同时运行呢? 这就使用了多线程了,所以,定义两个线程,一个是负责生产者的,一个是负责操作者的。

2、测试上面的代码发现,程序只运行了一次,所以为了让程序能够多次运行下去,就让生产者和消费者在调用资源的时候无线循环下去,这个时候我们又发现:

当商品还没有被生产出来的时候就被消费了,而且在消费的时候,是某一个商品上在循环的消费,这个是什么原因?这个是因为线程的执行权是由cup决定的,当某一个线程拿到cup执行权的时候,可能会一直执行多次,那怎么能让每次就执行一次呢?也就是说只要生产出来的产品没有被消费掉那么就不生产直到被消费,而且当消费完产品后,没有产品可消费了,程序也就不再消费让其等待直到有产品,这时可以加上判断,定义flag,当满足条件是就生产or消费否则让其wait,当其中有一个程序在等待的时候就唤醒另外一个程序,并改变flag的值.还要在函数上面加上同步
synchronized。加上同步是为了让消费只能在生产的后面。

3.现在的程序是单生产,单消费,那么如果把他改为多生产,多消费结果会怎么样?这时可以在创建两个线程,这个时候又出现了一个个情况就是,生产者还会在生产出来的产品还没有消费还在继续生产的,而消费者呢也是在重复的消费着同一个商品》这是为什么呢?通过分析发现,这个问题的根源还是由于cup随机性造成的当if判断不服个条件时,程序就处于等待状态,而cpu会随机的将执行权交给其他任意的一个线程去执行,而当这个线程也处于wait状态的时候,唤醒的可能是刚刚在等待的那个线程,此时,正好cpu将执行权交给了他,所以当之前等待的线程拥有执行权的时候,他就不会在判断if语句了而是直接继续执行下面的代码,这个时候异常就会发生了,当一直在生产商品的时候,就说明了是线程1和线程2在一直在交替循环。同理,当一直在消费产品的时候就是线程3和线程4在交替循环。这问题的主要原因就是被唤醒的线程没有做判断?
那么怎么解决呢? 可以将if改为while,改完后发现,程序处于死锁状态了,这个又是为什么呢?当其他三个线程都处于等待状态时,只有一个是活跃状态,这时,当cup还把执行权给他的时候,此时以判断不满足条件了,那么这个线程就会处于等待状态了,这样的话,四个线程就都处于等待状态了,所以就发生了死锁,这个原因是由于每一次都值只唤醒一个线程而导致的,所以我们可以将等待的线程全部唤醒(notifyAll),让其都有执行资格,一旦在执行的线程被等待后,cpu就可以将执行权交给其他的线程了,这样就不会发生死锁的情况了。这些wait
notify notifyAll等方法都在jdk1.4以前的,这样做虽然问题得到了解决,但是效率却降低了,直到jdk1.5之后,java给我们提供了一些新的解决方法就是用lock取代了synchronized , jdk1.5之后将监视器封装到了Conditions中,并且给我们提供了newCondition()的方法。

import java.util.concurrent.locks.*;

//定义一个资源类
class Resource
{
private String name;//资源应该以创建就会有名字和编号的
private int count=1;
private boolean flag = false;//定义一个flag用与判断是否符合条件
//定义一个接收name的set方法
private Lock lock = new ReentrantLock();
private Condition con = lock.newCondition();

public /*synchronized*/ void setResource(String name){
//		if(flag)
lock.lock();
try{
while(flag)
try{/*this.wait()*/con.await();}catch(InterruptedException e){}//当条件不满足是就让其在这里等待
this.name=name+".."+count;//把接收的name赋值给name,因为产品一出现就会有编号所以把count也一起赋值给name
count++;//count自增一次,因为下次在生产商品的时候,编号是增加的
//把产品和编号打印出来,为了看的更具体,我们就把调用的线程的名字也打印出来
System.out.println(Thread.currentThread().getName()+"...................生产者......"+this.name);
flag=true;//执行完一次后就将flag的值改掉,这样可以保证其只运行一次
//notify();
//			notifyAll();
con.signalAll();
}finally{
lock.unlock();
}
}

//在定义一个将生产出来的产品返回去函数,相当于被消费的
public /*synchronized*/ void getResource(){
//		if(!flag)
lock.lock();
try{
while(!flag)
try{/*this.wait()*/con.await();}catch(InterruptedException e){}//当条件不满足是让其在这里等待直到条件满足
//将生产出来的商品打印出来,同时也打印调用的那个线程
System.out.println(Thread.currentThread().getName()+".....消费者........................"+name);
flag=false;//执行完后将flag的值改掉,这样保证其只运行一次
//notify();
//			notifyAll();
con.signalAll();
}finally{
lock.unlock();
}
}
}

//定义一个生产者
class Producer implements Runnable
{
Resource r;
Producer(Resource r){
this.r=r;
}
//复写Runnable中的run方法
public void run(){
while(true)
r.setResource("汽水");//为了让其无线生产下去,加上一个while的无线循环
}
}
//定义一个消费者
class Consumer implements Runnable
{
Resource r;
//对象已建立就应该有资源的
Consumer(Resource r){
this.r=r;
}
//复写Runnable中的run方法
public void run(){
while(true)
r.getResource();//为了让其无线生产下去,加上一个while的无线循环
}
}

//创建线程
class ProducerConsumerDemoDemo
{
public static void main(String[] args){
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);//t1线程负责控制生产者
Thread t2 = new Thread(pro);//t2线程负责控制生产者
Thread t3 = new Thread(con);//t3线程负责控制消费者
Thread t4 = new Thread(con);//t4线程负责控制消费者
t1.start();
t2.start();
t3.start();
t4.start();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: