Java多线程10 多线程间的通信————等待唤醒机制(synchronized)

wait():————————让线程处于冻结状态,释放CPU的执行权和执行资格,将线程暂时存储到线程池中

API中的解释:

导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。 换句话说,这个方法的行为就好像简单地执行呼叫wait(0)

当前的线程必须拥有该对象的显示器。 该线程释放此监视器的所有权,并等待另一个线程通知等待该对象监视器的线程通过调用notify方法或notifyAll方法notifyAll 。 然后线程等待,直到它可以重新获得监视器的所有权并恢复执行

notify():会唤醒线程池中任意一个线程

API中的解释:

      • public final void notify()
        唤醒正在等待对象监视器的单个线程。 如果任何线程正在等待这个对象,其中一个被选择被唤醒。 选择是任意的,并且由实施的判断发生。 线程通过调用wait方法之一等待对象的监视器。

        唤醒的线程将无法继续,直到当前线程放弃此对象上的锁定为止。 唤醒的线程将以通常的方式与任何其他线程竞争,这些线程可能正在积极地竞争在该对象上进行同步; 例如,唤醒的线程在下一个锁定该对象的线程中没有可靠的权限或缺点。

notifyall():会唤醒线程池中的所有线程

  • 一个锁对应一个线程池子,锁是同步中对应的锁。

这几个方法必须使用在同步中,因为必须要标识,wait()    notify()    等方法所属的锁,同一个锁上的notify()   只能唤醒该锁上被wait()  的线程。

解决一个小问题:在查找API文档时,这几个方法不在Thread 类中,而在Object  类中,这是因为:调用这几个方法一定要标识的锁(其实就是用一个对象去调用的形式,),任意对象都能调用的方法必定是Object 类中的方法。

———————————等待唤醒机制应用/多线程之间的通信—————————————

根据生产者消费者的例子:进行多生产多消费的多线程间的通信联系(问题分析很重要)

1. 增加线程——————导致线程安全问题。生产了商品没有被消费或者是一个商品被多次消费

 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();

问题分析:(根据消费者 生产者代码)

t1 完整执行代码更改标记(商品1),唤醒t2(其实是唤醒 t2 t3 t4 中的任意一个),假设此时CPU没有切换,t1 继续执行,判断标记时t1 停止,t2 判断标记后也停止,假设接下来(1)t3 执行,t3 的二次执行时停止,t4  也停止了唤醒了t1 ,产生了商品2,改变标记,此时唤醒了t2 ,t2不用在判断标记,直接执行后面的代码,生产商品3,此时数据就会出现问题,商品2 虽被生产出来但是却没有被消费————产生安全问题

仔细分析产生问题的原因:线程没有判断标记————解决办法,将if   换成  while。记住在多线程通信中,都使用 while。

2.修改代码后会发现另一个问题:会出现死锁(所有线程都停住)

问题分析:就上段分析更改代码后,此时唤醒t2  ,t2 反复判断标记,flag==true;  此时所有的 线程都停止。出现死锁。

解决办法:

首先的思路是不唤醒同一方的线程(消费者就属于同一方),但是没有这样的解决代码

因此在唤醒时,就唤醒所有的线程,就算出现上述  :  t1  唤醒t2 这种时候,此时t1 唤醒了所有线程,执行权在t2 时 t2 会停止,但是此时活着的线程还有t3 t4.执行权会切换,就不会出现死锁现象。

不唤醒同一方的线程(消费者就属于同一方)这个在JDK1.5及其以上的版本中是有解决办法的(见下一章笔记)

//注意,开始时:线程池中没有等待的线程,notify() 其实就是空叫醒

还有当一个线程执行完一次完整代码时,他任然有可能继续拥有执行权,这条线程继续执行

代码:

class Resource{//简化程序-设资源只有一个//多线程之间的通信
    private String name;
    private int number=1;
    private boolean flag;
    //提供设置方法
    public synchronized void set(String name ){
        while(flag){//先判断
            try{wait();}catch(InterruptedException e){}//存在商品,此线程停止
        }

            //给成员变量赋值
            this.name = name + number;
            number++;
            //打印生产了那个商品
            System.out.println(Thread.currentThread().getName() + "    生产者    " + this.name);
            flag = true;//生产完,修改标志
            notifyAll() ;//唤醒消费者

    }
    //消费者的
    public synchronized void Out(){
        while(!flag){//先判断
            try{wait();}catch(InterruptedException e){}  //没有商品,消费者停止
        }
        System.out.println(Thread.currentThread().getName()+"   消费者    "+this.name);
        flag =false;//修改标记
       notifyAll();//唤醒生产者
    }
}
//描述生产者
class Producer implements Runnable{
    private Resource  r;
    Producer(Resource r){
        this.r = r;
    }
    public void run(){
        for(int x=0;x<5;x++) {
            r.set("面包");
        }
    }
}
//描述消费者
class consumer implements Runnable{
    private Resource r;
    consumer(Resource r){
        this.r=r;
    }
    public void run(){
        for(int x=0;x<5;x++) {
            r.Out();
        }
    }
}
public class producerAndConsumer {
    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(con);
        t1.start();
        t2.start();

    }
}

猜你喜欢

转载自blog.csdn.net/Stitch__/article/details/81701443