生产者与消费者

//notify();唤醒将对象锁让给我用的对象所在线程,当然用完了就通知对方收回了
//wait();将本对象所在的线程放置进入等待,同时释放次对象上的同步锁synchronized
//此对象在其他线程中可以用,就是wait别人用完了还给我啊
//此两种方法都是对对方
// synchronized(b){...};的意思是定义一个同步块,使用b作为资源锁。b.wait();//的意思是临时释放锁,并阻塞当前线程,好让其他使用同一把锁的线程有机会执行,在这//里要用同一把锁的就是b线程本身.这个线程在执行到一定地方后用notify()通知wait//的线程,锁已经用完,待notify()所在的同步块运行完之后,wait所在的线程就可以//继续执行.
//而且实际上,我们也只能在同步方法或者同步块里面调用wait()和notify().
//同一个副本在不同的线程里面被用,同步块(加锁的只能在一个线程中用,其他的可以在其他线程中用),有了这个基础之上,为了





1、synchronized关键字的作用域有二种:

1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例(不是同一个对象副本)中的synchronized方法;
2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。

2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象;

3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法;

Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

     一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

     二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。

     三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

     四、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

     五、以上规则对其它对象锁同样适用.

////即只有实际被锁的那个东西才不会在多线程中同时被用,看当前锁类型的粒度,是这个对象(单例),还是这个类。

可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj

在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。

小结如下:

搞清楚synchronized锁定的是哪个对象,就能帮助我们设计更安全的多线程程序。


synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的Java线程同步方能获得该锁,重新进入可执行状态。



/*@author shijin
* 生产者与消费者模型中,要保证以下几点:
* 1 同一时间内只能有一个生产者生产     生产方法加锁sychronized
* 2 同一时间内只能有一个消费者消费     消费方法加锁sychronized
* 3 生产者生产的同时消费者不能消费     生产方法加锁sychronized
* 4 消费者消费的同时生产者不能生产     消费方法加锁sychronized
* 5 共享空间空时消费者不能继续消费     消费前循环判断是否为空,空的话将该线程wait,释放锁允许其他同步方法执行
* 6 共享空间满时生产者不能继续生产     生产前循环判断是否为满,满的话将该线程wait,释放锁允许其他同步方法执行   
*/ 
 
//主类  
class  ProducerConsumer 

    public static void main(String[] args)  
    { 
        StackBasket s = new StackBasket(); 
        Producer p = new Producer(s); 
        Consumer c = new Consumer(s); 
        Thread tp = new Thread(p);  //线程的开始要,放到线程中做依托
        Thread tc = new Thread(c); 
       tp.start(); 
        tc.start(); 
    } 

 
//  
class Mantou 

    private int id; 
     
    Mantou(int id){ 
        this.id = id; 
    } 
 
    public String toString(){ 
        return "Mantou :" + id; 
    } 

 
//共享栈空间  
class StackBasket 

    Mantou sm[] = new Mantou[6]; 
    int index = 0; 
     
    /** 
    * show 生产方法.
    * show 该方法为同步方法,持有方法锁;
    * show 首先循环判断满否,满的话使该线程等待,释放同步方法锁,允许消费;
    * show 当不满时首先唤醒正在等待的消费方法,但是也只能让其进入就绪状态,
    * show 等生产结束释放同步方法锁后消费才能持有该锁进行消费
    * @param m 元素
    * @return 没有返回值 
    */  
 
    public synchronized void push(Mantou m){ 
        try{ 
            while(index == sm.length){ 
                System.out.println("!!!!!!!!!生产满了!!!!!!!!!"); 
                this.wait();  //将当前线程停止,释放这个对象上的锁,不再往下面执行
            } 
            this.notify();//唤醒其他要用此对象的线程,让其他线程获得此对象的锁,包括其他线程调用同一方法,当然也有调用其他不同的方法,符合条件就调用,不符合就等待,释放,让其他线程获取锁执行
        }catch(InterruptedException e){ 
            e.printStackTrace(); 
        }catch(IllegalMonitorStateException e){ 
            e.printStackTrace(); 
        } 
         
        sm[index] = m; 
        index++; 
        System.out.println("生产了:" + m + " 共" + index + "个馒头"); 
    } 
 
    /** 
    * show 消费方法
    * show 该方法为同步方法,持有方法锁
    * show 首先循环判断空否,空的话使该线程等待,释放同步方法锁,允许生产;
    * show 当不空时首先唤醒正在等待的生产方法,但是也只能让其进入就绪状态
    * show 等消费结束释放同步方法锁后生产才能持有该锁进行生产
    * @param b true 表示显示,false 表示隐藏 
    * @return 没有返回值 
    */  
    public synchronized Mantou pop(){ 
        try{ 
            while(index == 0){ 
                System.out.println("!!!!!!!!!消费光了!!!!!!!!!"); 
                this.wait(); 
            } 
            this.notify(); 
       }catch(InterruptedException e){ 
            e.printStackTrace(); 
        }catch(IllegalMonitorStateException e){ 
            e.printStackTrace(); 
        } 
        index--; 
        System.out.println("消费了:---------" + sm[index] + " 共" + index + "个馒头"); 
        return sm[index]; 
    } 

 
class Producer implements Runnable 

    StackBasket ss = new StackBasket(); 
    Producer(StackBasket ss){ 
        this.ss = ss; 
    } 
 
    /** 
    * show 生产进程. 
    */  
    public void run(){ 
        for(int i = 0;i < 20;i++){ 
            Mantou m = new Mantou(i); 
            ss.push(m); 
//          System.out.println("生产了:" + m + " 共" + ss.index
"个馒头");  
//          在上面一行进行测试是不妥的,对index的访问应该在原子操作里,因为可能在push之后此输出之前又消费了,会产生输出混乱  
            try{ 
                Thread.sleep((int)(Math.random()*500)); 
            }catch(InterruptedException e){ 
                e.printStackTrace(); 
           } 
        } 
    } 

 
class Consumer implements Runnable 

    StackBasket ss = new StackBasket(); 
    Consumer(StackBasket ss){ 
        this.ss = ss; 
    } 
 
    /** 
    * show 消费进程.
    */  
    public void run(){ 
        for(int i = 0;i < 20;i++){ 
            Mantou m = ss.pop(); 
//          System.out.println("消费了:---------" + m + " 共" + ss.index + "个馒头");  
//  同上  在上面一行进行测试也是不妥的,对index的访问应该在原子操作里,因为可能在pop之后此输出之前又生产了,会产生输出混乱  
            try{ 
                Thread.sleep((int)(Math.random()*1000)); 
            }catch(InterruptedException e){ 
                e.printStackTrace(); 
            } 
        } 
    } 


//由于本程序,设置了最多20次循环,所以进行20次就自动停止,消费的频率低于生产,所以最后留下的是消费的单独进行。

猜你喜欢

转载自yuhuiblog6338999322098842.iteye.com/blog/2176608