代码:
public class TT implements Runnable { int b = 100;
@Override
public void run() {
m1();
}

public synchronized void m1() {
b = 1000;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1---" + b);
}

public synchronized void m2() {

try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
b = 2000;
//System.out.println("m2---" + b);   //标号1
}

public static void main(String[] args) {
TT t = new TT();
new Thread(t).start();      //标号2

t.m2();
System.out.println("main---" + t.b);  //标号3
}
}
第一种执行结果:
main---1000
m1---1000第二种执行结果:(加上标号1后)
m2---2000
main---2000
m1---1000问题:
1.加上标号1之后结果为什么就变了呢?
  我认为是:执行到标号2,m1线程开启,去执行m1方法,
  同时main方法这个线程还继续向下执行。因为m1拿到这把锁,于是m2当前无法对变量b进行修改。
  m2必须等着m1,直到m1执行完毕才有机会执行m2。这时m1 sleep(1000),程序继续执行,现在执行到
  标号3,打印main---1000。然后1000毫秒之后,m1的打印语句执行m1---1000。接着再执行m2即m2---2000。
  所以结果是:
  main---1000
  m1---1000
  m2---2000
对synchronized理解不好。希望大家赐教。谢谢。

解决方案 »

  1.   

    int是基础类型,不会加锁的,换成integer
      

  2.   

    第一种
    因为调用m2是直接调用方法,没用到线程调度
    所以在main里面m2执行好了再输出
    然后执行new Thread(t).start();
    所以结果是1000,1000
    第二种
    因为m2是直接调用方法,和synchronized没关系,输出m2---2000,和main的main---2000
    所以执行好main再执行m1,输出m1---1000
    m2---2000
    main---2000
    m1---1000
      

  3.   

    这个和调用线程的start和调用方法的区别
    和b的类型没关系
    你的synchronized没用好public static void main(String[] args) {
            TT t = new TT();
            new Thread(t).start();      //标号2
            
           new Thread(t).start();      
            System.out.println("main---" + t.b);  //标号3
        }
    这样子你在试下,synchronized会起作用的,这样有一个“t”锁来共享和排斥
      

  4.   

    楼主
    public synchronized void m1() {
            b = 1000;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("m1---" + b);
        }
    synchronized 加在方法m1()上 只是这个方法同步了 (也就是不能同时对该方法操作)
    并不会影响m2()方法
    去调试将休眠时间设大点 那样更清楚
      

  5.   

    你的代码是没有问题的逻辑也是对的但是为什么结果不是我们所期望的呢?很简单线程欺骗了我们。
    public static void main(String[] args) {
            TT t = new TT();
            new Thread(t).start();      //标号2
            
            t.m2();
            System.out.println("main---" + t.b);  //标号3
        }
    看看你的代码
    我简单分析一下你的逻辑
      new Thread(t).start();//在你认为这句话会执行m1方法是吧
      t.m2();//这就话会执行m2方法  在我们初看并没有什么问题不过你忽略了一个问题(找点多线程的资料看看吧)
      new Thread(t).start();这句代码并不会立刻执行m1方法,这句代码知识说我开启了另一个线程不过这个线程什么时候可以执行还不确定这就去决议cpu的时间调度(最少在执行完这句代码是cpu还在执行我们的主函数这条线程)一般情况下会继续执行t.m2();
      也就是说我们代码实际执行的顺序并不是我们想象的那么先是m1然后是m2相反先是是m2后m1不过这也不一定如果刚执行玩new Thread(t).start();这句代码系统刚好调度停止执行main函数所在的线程刚好去执行我们新new出来的这个线程那么执行的顺序就会是先m1 方法后m2方法总结我们的代码在调用m1方法和调用m2方法的先后顺序是不确定的完全取决于系统的调度
    你可以将代码修改成
    public static void main(String[] args) {
            TT t = new TT();
            new Thread(t).start();      //标号2
            try {
                Thread.sleep(0);//即使我们只sleep了0系统也会去执行其他线程。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            t.m2();
            System.out.println("main---" + t.b);  //标号3
        } 这样就是一想要的结果了。
    建议可以给sleep的时间多点
    因为操作系统让线程执行的顺序是不确定的。
    try {
                Thread.sleep(0);//即使我们只sleep了0系统也会去执行其他线程。
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
      

  6.   

    不知道你的加锁目的。仅对结果做解释:1)你的synchronized加的是方法锁不是资源锁,所以你按1楼说的做是无效的。仅在加锁方法中对b进行修改,所以你对b的加锁其实可以是有效的。但是当你调用m1时,不会影响m2的调用。
    2)对这两句的说明:
       new Thread(t).start();  --这样做会启动一个线程
       t.m2();                 --这样做会在主线程中调用m2方法
       这两句谁先执行是未知的。最终谁最后执行完b的值为谁,main的值也就为谁。
    3)可能的结果:
       m2---必为2000 
       main---多为2000,也可能为1000 (但我个人觉得为2000的可能性更高)
       m1---必为1000
    4)可能的顺序:
       main必在m2之后
       m1在main之后和m2之前的可能性均有,且较高(m1,m2,main或m2,main,m1)
       m1可能在main和m2之间(m2,m1,main)
    5)怎么也得给我50分吧?要是还不清楚我再拿那50分。
      

  7.   

    http://topic.csdn.net/u/20091213/19/7a98cbe8-cd5f-40e0-8401-8e8f56c472ca.html?41365
      

  8.   

    执行的时候        System.out.println("main---" + t.b);  //标号3
    有可能是2000也有可能是1000 
    取决于 m1中的 b = 1000;
    如果在它之前被执行 就是2000 之后就是1000
      

  9.   

    我错了
    3)可能的结果:
        m2---必为2000  ---99%为2000
        main---多为2000,也可能为1000 (但我个人觉得为2000的可能性更高)
        m1---必为1000  ---99%为1000
    刚好执行一句会产生JVM分时的可能也是存在的。
      

  10.   

    你这个main函数根本没用到多线程,所以synchronized 根本没起作用。
    而m2为2000只是几率问题,你把那个时间改一下结果可能不一样。
      

  11.   


    jvm会对同一个对象的所有同步块或同步方法的,并不是针对某个方法或块。在这个例子里面同一个对象是不可能同时进入m1和m2的。
      

  12.   

    1.加上标号1之后结果为什么就变了呢? 
    A:跟你加不加标号1没什么关系,是个线程调度随机现象。我认为是:执行到标号2,m1线程开启,去执行m1方法, 
    A:这句话不对,并不是线程.start了,它的run方法就马上执行,这是个线程调度的问题。如果你确实需要该线程得到cpu开始执行,可以加个Thread.yeild();    public static void main(String[] args) {
            TT t = new TT();
            new Thread(t).start();      //标号2
            Thead.yeild();  //Added line.
            t.m2();
            System.out.println("main---" + t.b);  //标号3
        }
    同时main方法这个线程还继续向下执行。因为m1拿到这把锁,于是m2当前无法对变量b进行修改。。
    A:须知当某个线程阻塞了,如Sleep或wait,它拿到的锁就释放了.
      

  13.   

    A:须知当某个线程阻塞了,如Sleep或wait,它拿到的锁就释放了. 
    对17#的这句话不敢同意。
      

  14.   

    public class TT implements Runnable {    int b = 100;    public void run() {
            m1();
        }
        
        public synchronized void m1() {
         System.out.println("aa");
        
            b = 1000;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("m1---" + b);
        }
        
        public synchronized void m2() {
            
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            b = 2000;
            //System.out.println("m2---" + b);   //标号1
        }
        
        public static void main(String[] args) {
            TT t = new TT();
            new Thread(t).start();      //标号2
            
            t.m2();
            System.out.println("main---" + t.b);  //标号3
        }
    }对LZ的m2方法sleep时间调的大了点。运行的效果就明显了。
    第一种结果:
    aa
    main---1000
    m1---1000
    解释:
    1。对象t在两个线程上运行:main和new Thread(t)建立的新线程 ,对于m1,m2方法加的锁synchronized 对于两个线程是都起作用的,m1,m2的监视器都是对象t。
    2。t.m2();和System.out.println("main---" + t.b);两个语句都是在main线程上执行的,t.m2()一定是在println语句之前。
    3。标号2语句只是启动了一个线程,但不一定去执行线程上的代码。
    4。t.m2()运行之后得到监视器并sleep,这时CPU对另一个线程(就叫做A吧)去执行,这时它发现监视器被占用A线程进入等待线程池。
    5。这时两个线程都处于等待状态,当t.m2() sleep完成之后,继续执行。方法m2执行完成之后释放监视器并唤醒等待监视器的线程A.并去执行它。
    6。当线程A修改b的值为1000,进入sleep状态,CPU转去执行线程main,并打印出1000。
    7。去执行线程A,A sleep完成之后打印出1000。在我这里第二种情况结果是:
    m2---2000
    aa
    main---1000
    m1---1000
    自己分析原因吧。和上面运行的过程是一样的。
      

  15.   

    谢谢21楼douchog_13k的回答。
    不过有两点还是需要再考虑下的。
    5。这时两个线程都处于等待状态,当t.m2() sleep完成之后,继续执行。方法m2执行完成之后释放监视器并唤醒等待监视器的线程A.并去执行它。 
    6。当线程A修改b的值为1000,进入sleep状态,CPU转去执行线程main,并打印出1000。方法m2执行完之后执行谁也是不确定的。
    1.有可能执行main,打印main---2000,再执行m1,打印m1---1000;
    2也有可能执行m1,在m1 sleep(1000)中执行main,打印main---1000,然后1秒过后打印m1---1000。所有会有两种结果:
    m2---2000
    main---2000
    m1---1000m2---2000
    main---1000
    m1---1000
      

  16.   

    赞成9楼说法,你把线程睡眠时间弄大点,让它多睡眠一会你就得到不同结果了。看CPU调度