java线程间通信



线程间通讯:
      其实就是多个线程在操作同一个资源,但是操作的动作不同

等待唤醒机制:
      wait
      notify
      notifyAll
      都是用在同步中,因为要对持有监视器(锁)的线程操作。
      所以要是用在同步中,因为只有同步才具有锁。 
      等待唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
生产者消费者实例(多个生产者,多个消费者):

class ProducerConsumerDemo{
      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);
            Thread t2 = new Thread(pro);
            Thread t3 = new Thread(con);
            Thread t4 = new Thread(con);
            t1.start();
            t2.start();
            t3.start();
            t4.start();
      }
}
class Resource{
      private String name;
      private int count = 1;
      private boolean flag = false;
      public synchronized void set(String name){
            //对线程只判断了一次,如果线程被释放,再获得了执行权,则不需要判断就可通过了,这是一个问题
            if(flag)
                  try{this.wait();}catch(Exception e){}
            this.name = name+"--"+count++;
            System.out.println(Thread.currentThread().getName()+"....生产者..."+this.name);
            flag = true;
            this.notify();
      }
      public synchronized void out(){
            if(!flag)
                  try{this.wait();}catch(Exception e){}
            System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
            flag = false;
            this.notify();
      }
}
//生产者
class Producer implements Runnable{
      private Resource res;
      Producer(Resource res){
            this.res = res;
      }
      public void run(){
            while(true){
                  res.set("+商品+");      
            }
      }
}
//消费者
class Consumer implements Runnable{
      private Resource res;
      Consumer(Resource res){
            this.res = res;
      }
      public void run(){
            while(true){
                  res.out();      
            }
      }
}
      此程序容易出现多次生产,单次消费。或单词生产多次消费的问题。
      问题产生原因:
            以上程序块中
                  设生产者:Thread-0Thread-1     消费者:Thread-2Thread-3
                  0线程先进入,flag=true1线程进入,因flag=true,等待
                  0线程再次进入,因flag=true,等待。
                  2线程进入,flag=false,并唤醒了0线程,01线程已经路过判断关卡,因此直接执行代码,
                  flag=true,并随机唤醒了1线程;1线程已经经过判断关卡,因此直接执行代码,所以会出现两次生产
                  一次消费的问题。

      如果改用whileflag)循环判断,则解决了线程只判断一次便通过的问题,但容易出现无限等待的问题,
      就是几个线程互相等待。如果不懂可以让代码跑起来试试。
      问题产生原因:
                  flag=false3线程进入,等待。
                  0线程进入,flag=true1线程进入,因flag=true,等待
                  0线程再次进入,因flag=true,等待。
                  2线程进入,flag=false,随机释放3线程,2线程继续运行,因flag=false,结果处于等待。
                  因flag=false3线程也处于等待,因此所有的线程全部等待,无人激活
      解决问题:
            既不能让同步代码块只判断一次便通过,又不能让程序进入无线等待中。
            综上,采用notifyAll()方法,将所有的线程全部唤醒,而不是仅仅只唤醒一个。这样一来
            既可以无限判断,不至于有漏网之鱼,又免去无限等待的痛苦。
            修改如下:
            public synchronized void set(String name){
                  while(flag)
                        try{this.wait();}catch(Exception e){}
                  this.name = name+"--"+count++;
                  System.out.println(Thread.currentThread().getName()+"....生产者..."+this.name);
                  flag = true;
                  this.notifyAll();
            }
            public synchronized void out(){
                  while(!flag)
                        try{this.wait();}catch(Exception e){}
                  System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
                  flag = false;
                  this.notifyAll();
            }
JDK5.0升级以后的多线程通信
      其中用到了 Lock , Condition 类;以下代码与Lockapi中示例代码相似
      private Lock lock = new Lock();
      Condition condition_pro = lock.newCondition();
      Condition condition_con = lock.newCondition();
      //生产者生产方法
      public void set(String name)throws InterruptedException{
            //获取锁
            lock.lock();
            try{
                  while(flag)
                        condition_pro.await();//生产者等待
                  this.name = name+"--"+count++;
                  System.out.println(Thread.currentThread().getName()+"....生产者..."+this.name);
                  flag = true;
                  //唤醒消费者(唤醒对方)
                  condition_con.signal();
            }finally{
                  //释放锁
                  lock.unlock();
            }

      }
      //消费者消费方法
      public void out()throws InterruptedException{
            lock.lock();
            try{
                  while(!flag)
                        condition_con.await();//消费者等待
                  System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
                  flag = false;
                  //唤醒生产者(唤醒对方)
                  condition_pro.signal();
            }finally{
                  lock.unlock();
            }
      }
JDK5.0升级后的新特性:
      jdk1.5中提供了多线程升级解决方案。
      将同步synchronized替换成了显示的lock操作。
      将Object中的waitnotifynotifyAll,替换成了Condition对象,该对象可以通过Lock锁进行获取。

如何让线程停止:
      原理:只有一种,run方法结束。
      开启多线程运行,运行代码通常是循环结构。
      只要控制住循环,就可以让run方法结束,也就是线程结束。
  特殊情况:
      当线程处于了冻结状态,就不会读取到结束标记,线程就不会结束。
      此时只能强制让线程恢复到运行状态中来,interrupt()就是具有这样功能的方法。
Thread类中的一些常用方法:
      interrupt()中断方法:该方法是结束线程的冻结状态,使线程回到运行状态来。
      setDaemon(boolean on):将该线程标记为守护线程或用户线程(后台线程)。此方法必须在开启线程之前调用,具体可以写实例试试
      join(): 该方法用于线程获得cpu(执行权),直到该线程终止。
            join例子:
            Demo d = new Demo();//创建一个Runnable子类对象
            Thread t1 = new Thread(d);
            Thread t2 = new Thread(d);
            t1.start();
            //此处t1线程调用join方法,获得main线程的执行权,main线程暂停服务,直到t1线程执行结束
            //由于main线程失去了执行权,所以t2线程还没有被开启。
            t1.join();
            t2.start();
      优先级问题:
            Demo d = new Demo();//创建一个Runnable子类对象
            Thread t1 = new Thread(d);
            Thread t2 = new Thread(d);
            t1.start();
            //设置线程优先级
            t1.setPriority(Thread.MAX_PRIORITY);
            t2.start();
      yield() :暂停当前正在执行的线程对象,并执行其他线程。
            稍微暂停当前线程,并执行其他线程,放在run函数中,有使得线程交叉运行的效果;




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值