问一个关于synchronized关键字的小问题,先贴代码。。如下。。public class tmp{
   public static void main (String[] args) throws Throwable {
   Product p=new Product();
   new Consumer(p);
   new Productor(p);
   }
}class Product{
boolean ispop=true;
private int i=0;
synchronized void push(){
try{
if(!ispop)wait();
i++;
ispop=false;
showi();
notify();
}catch(Exception e){
System.out.println("error in push---"+e);
}
}
synchronized void pop(){
try{
if(ispop)wait();
i--;
ispop=true;
showi();
notify();
}catch(Exception e){
System.out.println("error in pop---"+e);
}
}
void showi(){
System.out.println("Now the i is "+i);
}
}
class Consumer implements Runnable{
Product p;
Consumer(Product p){
this.p=p;
new Thread(this).start();
}
public void run(){
try{
for(int i=0;i<10;i++){
p.pop();Thread.sleep(150);
}
}catch(Exception e){
System.out.println("error--");
}
}
}
class Productor implements Runnable{
Product p;
Productor(Product p){
this.p = p;
new Thread(this).start();
}
public void run(){
try{
for(int i=0;i<10;i++){
p.push();Thread.sleep(200);
}
}catch(Exception e){
System.out.println("error--");
}
}
}为什么在pop和push前面要加上synchronized关键字,synchronized关键字不是锁定方法用的吗?在这里,锁定方法似乎没有意义吧,因为方法调用的顺序是由wait和notify来控制的了。可是去掉synchronized关键字编译就会报错。。

解决方案 »

  1.   

    synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
      

  2.   

    线程同步的问题,pop和push就像数据库的事务处理,需要加锁避免出错!
      

  3.   

    wait和notify是基于什么实现的?不就是锁吗?如果没有锁,怎么wait和notify?
      

  4.   

    呃。。那个。。请问wait和notify是怎么实现的?如果考虑到如何实现的问题,那么不是太复杂了?我学的那本书里面的说到synchronized的时候是这么用到:class Callme{
    synchronized void call(String msg){
    System.out.print("["+msg);
    try{
    Thread.sleep(1000);
    }catch(InterruptedException e){
    System.out.println("Interrupted");
    }
    System.out.println("]");
    }
    }
    class Caller implements Runnable{
    String msg;
    Callme target;
    Thread t;
    public Caller(Callme targ,String s){
    target=targ;
    msg=s;
    t=new Thread(this);
    t.start();
    }
    public void run(){
    target.call(msg);
    }
    }public class tmp{
    public static void main (String[] args) {
    Callme target=new Callme();
    Caller ob1=new Caller(target,"hello1");
    Caller ob2=new Caller(target,"hello2");
    Caller ob3=new Caller(target,"hello3");
    }
    }很显然,这里的synchronized的功能就是让call被调用的时候别人不能在调用,所以call就能完整的结束从而实现输出字符串的规则性。所以我把synchronized理解成是锁住某个方法让其在同一时刻只被一个线程调用。那么,在这个理解的基础上看待前面的例子。首先是new Consumer(p)里的线程调用pop执行,因为ispop是true,所以该线程就暂停了;而另一个线程则是相反,能够执行i++然后notify唤醒前面Consumer的线程而同时把自己wait了,Consumer被唤醒之后也做着和Productor类似的事儿,就这样不断循环到结束。在这里,还需要synchronized来锁住独占方法吗?貌似这跟锁不锁方法没有关系了吧。上面Caller这个例子里的是同一个方法内的次序问题,所以为了保证方法能被顺利执行到底所以才用synchronized的锁定,而这里是两个方法之间的次序问题,所以用标志ispop+wait+notify来维护次序。然后,如果wait和notify是要有锁的情况下才可以工作,那么,java为什么不默认就附上synchronized修饰符?!因为没了锁wait和notify就不可用,那在存在wait和notify的方法锁就是必须的,既然必须了,何必还要手动打synchronized?
    而且,如果要考虑到语言的实现细节才能使用语言,那是不是散失了语言的简洁性了?呃。。好复杂。。我现在都搞晕了。。
      

  5.   

    首先要搞清楚,你这里的wait notify 调用的是this的方法,你在方法写的synchronized关键字是对this加上锁。
    你要是不加锁就调用wait(),肯定不行当然你可以这样写
    void push(){
         synchronized (this) {
            try{
                if(!ispop)wait();
                i++;
                ispop=false;
                showi();
                notify();
            }catch(Exception e){
                System.out.println("error in push---"+e);
            }
         }    
        }
       void pop(){
       synchronized (this) {
            try{
                if(ispop)wait();
                i--;
                ispop=true;
                showi();
                notify();
            }catch(Exception e){
                System.out.println("error in pop---"+e);
            }
       } 
        }
    ------------------------------------
    对方法synchronized不像你理解的就不能同时进入这个方法,Product p1 = new Product ();Product p2 = new Product ();p1,p2是不同的对象,就可以同时进入。
    ---------------------------------------------
    如果方法都默认synchornized那么肯定有性能问题,毕竟需要它的方法只是少数
      

  6.   

    知道,这里的wait和notify是继承自Object的方法,当在类内直接调用wait的时候就是调用当前对象的wait方法了。
    ---------------------------------------------------------------------------
    这个我也知道,不同对象那么其成员也是相互独立的,所以p1、p2可以同时进入。但是这里锁着的是同一个对象p哦,所以不用关系到另外的p2去吧。
    ---------------------------------------------------------------------------
    如果方法内含有wait和notify,那么可以不默认为synchronized吗?呵呵,这才是我想表达的。
    (*^__^*) 嘻嘻……还是这个问题,为什么调用wait和notify的时候要显式的synchronized?如果是不得不显式标注的话,那么便意味着存在不标注synchronized的情况,不然就真的是多此一举了。哪位高手知道答案么?如果有不显示标注synchronized的例子,能不能举个例子给俺呀,在此谢过
      

  7.   

          wait()只能在同步语句块里调用,现在你明白了吗?
      

  8.   

        
    要想调用wait()就得先获得对象锁(所以wait()只能在同步语句块里调用),如果一个线程 wait()它就会在释放对象锁
    的同时进入等待池,除非有线程触发notify()才能把它从等待池中释放出来
    两种情况:
    运行状态--(synchronized)----->得到对象锁--------(wait())------->等待池---(外部的notify())---->synchronized外等待等待
                        得不到就一直等待(在对象锁池)      没notify()就一直在等待池里等着
    (除非它再次获得对象锁)
    运行状态--synchronized------->进入对象锁池----得到对象锁--->就绪状态

        
      

  9.   

    知道了。是不是说在wait和notify执行的时刻,也就是等待池列表改变的时刻,必须获得对象锁以保证等待池列表的正确性?可是,既然必须获得锁,那么,为什么Java不自动在wait的时候获得锁而要把这个细节曝露出来呢?呵呵,问题基本已经理解了,谢谢各位的帮忙哈~~
      

  10.   

           JAVA中,一个线程只要wait()它就必须得释放它已经得到的对象锁,如果不释放,就有可能造成死锁
           JAVA线程中只有在下面4种情况下才能放锁:
           1>同步语句块结束
           2>当break,return跳出同步语句块
           3>有未捕获得导常
           4>调用wait()
      

  11.   

         避免死锁:
         1>顺序上锁(A->B->C->D)
         2>禁止回锁
         3>反向解锁