public class TT implements Runnable {
int b = 100;

public synchronized void m1() throws Exception{
//Thread.sleep(2000);
b = 1000;
Thread.sleep(5000);
System.out.println("b = " + b);
}

public synchronized void m2() throws Exception {
Thread.sleep(2500);
b = 2000;
}

public void run() {
try {
m1();
} catch(Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) throws Exception {
TT tt = new TT();
Thread t = new Thread(tt);
t.start();

tt.m2();
System.out.println(tt.b);
}
}
结果是:
1000
b = 1000
为什么顺序是m2()---m1() 执行m1()的时候打印tt.b 而不是在执行m2()的时候就打印?

解决方案 »

  1.   


    public class TT implements Runnable {
    int b = 100;
    public synchronized void m1() throws Exception{
    //Thread.sleep(2000);
    b = 1000;
    Thread.sleep(500);
    System.out.println("b = " + b);
    } public synchronized void m2() throws Exception {
    Thread.sleep(250);
    b = 2000;
    }
    public void run() {
    try {
    m1();
    } catch(Exception e) {
    e.printStackTrace();
    }
    }
    public static void main(String[] args) throws Exception {
    TT tt = new TT();
    Thread t = new Thread(tt);

    t.start();//线程t启动,执行run(),执行m1(),b=1000,开始睡5000

    tt.m2();//同时主线程启动,执行m2(),睡2500,醒了以后b=2000,此时线程t还在睡着,主线程接着往下执行

    System.out.println(tt.b);//主线程打印输出,此时的b是2000。

    //5000以后线程t睡醒了,打印输出 b = 2000
    }
    }
    按照正常的分析,似乎应该是注释当中的情形,但是实际上很不幸,根本不是!
    多次执行上面的程序你就会发现,输出结果可能是:
    1000
    b = 1000
    也可能是:
    2000
    b = 1000
    原因是什么呢?这与java的内存模型有关系。1.java的内存模型分为主存储器与工作存储器两种:
    (1)主存储器就是实例位置所在的区域,所有的实例都存在于主存储器内,
    尤其是,实例所拥有的字段即位于主存储器内的区域,主存储器被所有的线程所共有。 
    (2)工作存储器是各个线程所拥有的作业区,所有的线程都有自己专有的工作存储器,
    在工作存储器以内存在主存储器的必要部分的拷贝,称之为工作拷贝。 
    2.字段(field)的引用 
    线程无法对主存储器进行直接的操作,因此无法直接引用字段的值。 
    当线程要引用字段的值的时候,会一次将值从主存储器拷贝到工作存储器上,拷贝完成以后,线程就可以引用工作存储器里里面的值了。 
    当同一个线程再次引用这个值的时候,它有可能还是从主存储器拷贝以后在引用,也可能使用自己以前拷贝过来的值,这取决于jvm。 
    3.字段(field)的修改 
    线程无法对主存储器进行直接的操作,因此无法直接修改字段的值。 
    当线程要把值指定给字段的时候,会一次将值指定给工作存储器上的工作拷贝。指定完成以后,工作拷贝的内容便会影像到主存储器,至于合适会影像到主存储器取决于jvm。 
    当同一个线程反复修改同一个字段的时候,会发生什么情况?有可能只是反复的修改工作拷贝,最后的结果影像到主存储器,也可能每次修改以后都拷贝到主存储器,这取决于jvm。参考:[code=HTML]<a href="http://user.qzone.qq.com/605162215/infocenter?ptlang=2052&ADUIN=605162215&ADSESSION=1274251834&ADTAG=CLIENT.QQ.2587_Mysrv.0">http://user.qzone.qq.com/605162215/infocenter?ptlang=2052&ADUIN=605162215&ADSESSION=1274251834&ADTAG=CLIENT.QQ.2587_Mysrv.0</a>
    [/code]
      

  2.   

    回楼上 t.start()以后 调用了m1()方法 这个方法是synchronized锁住的 m2()方法还能用吗?
      

  3.   

    你只锁住了m1(),m2()又不在m1()的synchronized块中,怎么可能锁的住嘛。
     public synchronized void m1() 
     public synchronized void m2()
    是2个同步块,互相之间没关系的。 
      

  4.   


    //上次没看到那个synchronized,你看看这样解释可以吗?
    public class TT implements Runnable {
    int b = 100;
    public synchronized void m1() throws Exception{
    //Thread.sleep(2000);
    b = 1000;
    Thread.sleep(500);
    System.out.println("b = " + b);
    } public synchronized void m2() throws Exception {
    Thread.sleep(250);
    b = 2000;
    }
    public void run() {
    try {
    m1();
    } catch(Exception e) {
    e.printStackTrace();
    }
    }
    public static void main(String[] args) throws Exception {

        long start=System.currentTimeMillis(); //1

    TT tt = new TT();                     //2
    Thread t = new Thread(tt);            //3 t.start();//4    线程t启动,但不一定立即就获得cpu,立即执行run()方法

    tt.m2();//5      如果是主线程首先执行的话,执行m2(),此时线程t因为没有锁,被迫等待,
    //主线程睡2500,醒了以后b=2000。
    //退出m2()方法,释放锁,此时,等待的线程t获得锁,得以执行,主线程同时也继续执行。

    System.out.println(tt.b);//主线程打印输出,b已经被主线程改为了2000,很有可能此时被t线程改为了1000,也可能没改,取决于谁先执行

    t.join();//等待线程t结束

    long end=System.currentTimeMillis();

    System.out.println("耗时:"+(end - start));//输出:耗时:750

    //如果去掉synchronized,输出耗时:500,说明两个方法m1和m2的确是互斥的。
    }
    }
    //如果在第4行,t线程首先执行,主线程被迫等待,t执行完毕,b=1000,主线程继续往下执行。
      

  5.   


    问题是你调用start的时候只是调度了一个新线程,而这个线程并不是立即调用m1方法。而是根据CPU分配,有可能分配到主线程运行m2方法了
      

  6.   

    不好意思,调用那块看错了,我看成2个对象了这个就是主线程和子线程抢CPU分配的问题大侠们解释的很清楚了
      

  7.   

    ,刚好我也遇到这个问题。 但是如果在m1()和m2()前加一个打印语句的话,每次都是先执行m2() 这是为什么呢?public class TT implements Runnable {
    int b = 100; public synchronized void m1() throws Exception{
    //Thread.sleep(2000);
    System.out.println("m1() called");
    b = 1000;
    Thread.sleep(5000);
    System.out.println("b = " + b);
    } public synchronized void m2() throws Exception {
    System.out.println("m2() called");
    Thread.sleep(2500);
    b = 2000;
    } public void run() {
    try {
    m1();
    } catch(Exception e) {
    e.printStackTrace();
    }
    } public static void main(String[] args) throws Exception {
    TT tt = new TT();
    Thread t = new Thread(tt);
    t.start(); tt.m2();
    System.out.println(tt.b);
    }
    }运行结果:
    m2() called
    2000
    m1() called
    b = 1000或者m2() called
    m1() called
    2000
    b = 1000为什么每次都是 m2() called 先执行呢?