public class TT implements Runnable {
int b = 100;

public synchronized void m1() throws Exception{
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希望大家能详细解释下锁机制!

解决方案 »

  1.   

    刚才去测试了半天  知道怎么回事了 
    启动线程 调用线程的 public void run() {
    try {
    m1();
    } catch(Exception e) {
    e.printStackTrace();
    }
    }
     函数的时候时间片被主线程抢走了
    主线程打印完后再等里边的线程执行完后结束
      

  2.   

    谢谢楼上,学习了
    A:
    tt.m2()
    B:
    打印b
    C:
    看起来tt.m1()在ABC三个地方都有可能啊
      

  3.   

    lz看看synchronized关键字的作用:
    是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。再结合这个程序,思考一下
      

  4.   

    你这程序其实只有两条活动的线程,一个是主线程,一个是你的Thread t t.start();
    tt.m2();这两句谁先谁后执行真不好说的,但是可以肯定的是你一旦把tt.m2放到t.start()前面
    必然结果是2000 b=2000
    因为tt.m2并不是启动了新线程,所以一定是等到m2方法执行完之后才去执行t.start()
    至于你原来的写法我也说的很清楚了t.start();  
    tt.m2();
    //一直到t.start();这句代码之前你的活动线程只有一条,但是执行到这里的时候主线程只负责调用start方法启动线程,但是线程的run代码块里面的代码的执行主线程不管,由新线程自己去控制,所以主线程会直接执行tt.m2,问题就回到我刚才说的话题上了,t.start()在代码直观的看好像在tt.m2前面,但是实际上却不一定,从计算机的工作原理来分析的话,如果是单核心的CPU同一时间只能运行一个线程,双核的CPU物理逻辑上只能同时运行两条线程,我们看到计算机同时运行个几十上百条线程的壮观只不过计算机的处理速度非常快,产生的错觉是同时进行的罢了我给你个比你这个例子更容易理解的例子吧
    public class ThreadTest extends Thread{
    public void run(){
    for(int i=0;i<500;i++)
    go();
    }

    synchronized void go(){
    System.out.println(getName());
    try {
    sleep(100);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }


    public static void main(String[] args){
    new ThreadTest().start();
    new ThreadTest().start();
    new ThreadTest().start();
    new ThreadTest().start();
    new ThreadTest().start();
    new ThreadTest().start();
    new ThreadTest().start();
    new ThreadTest().run();
    }
    }
      

  5.   

    去运行下看看,go方法我加了synchronized
    线程的输出顺序并不是很整齐的吧?比如线程1进入go方法,确实其他线程都无法进入方法体,都被阻塞在外面等候,但是结果并不是
    Thread-1
    Thread-2
    Thread-3
    Thread-4
    Thread-5
    Thread-6
    Thread-7
    Thread-8
    Thread-9
    Thread-0
    然后重复循环的吧,这就是线程优先级的问题,同样的优先级谁先谁后虚拟机是自动分配的,而且获得执行权限的线程也不同,每个线程能持有的CPU的占用周期每次还都不一样,就记住优先级相同虚拟机随即分配先后除非你在程序里显示的控制线程的执行,例如wait notify notifyAll等等
      

  6.   

    TT tt = new TT();
    Thread t = new Thread(tt);  
    t.start();   // 1 main主线程中启动子线程,但子线程不一定立即执行tt.m2();     //2 main主线程中打印
    System.out.println(tt.b);
    这块代码你可以去测试,10次执行,10次都是2先执行,然后才是run中的打印。
    这就是多线程:代码执行不一定就是代码的先后顺序了。因为在这里至少有2个线程在跑。如果你一定要想让1处先打印,那么你就要让main线程等到1处子线程执行完。怎么搞?
    修改:
    在t.start()之后添加:
    t.join(); //让t这个线程执行完之后main线程才继续往下走,类似于vxWorks中的二进制信号量
    tt.m2();这样,就是你想要的结果。
      

  7.   

    我把tt.m2放到t.start()前结果是 2000  b=1000  !
      

  8.   


    多谢,学习了join()方法   但是还是没有弄清 synchronized的用途!
      

  9.   

    刚才从百度百科中看到这样的话:
    synchronized机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态。是这样的吗?那运行 m1() 方法的时候 m2() 无法运行,在 m1() sleep的时候 ,System.out.println(tt.b)执行,故而打印出 1000 ; 之后 m1() 中的 System.out.println("b = " + b) 执行 ,打印出 b=1000我这样解释对不?我是上周开始学Java的,所以可能理解起来比较困难。所以问的比较多,比较菜,希望大家理解!
      

  10.   

    之前没注意看你的代码,我以为在m2方法里面也有个输出,其实是想说线程启动之前的问题要想理解线程的话,最简单的最笨的方法就是画图,画个时序图
    图我不画了,这里程序的入口进去之后都是只有一条活动线程,就是主线程,然后这个时候你的代码是一行一行执行的,所以你的m2方法执行的时候根本谈不上需要锁,只有一条线程,只有当你启动了线程之后才有两条线程,但是哪条跑的更快些说不定的
    这里模拟下现在操作系统中有三条线程
    线程1 为你的程序主线程
    线程2 为你的程序主线程启动的第一条新线程
    操作系统其他线程(例如IE浏览器)            1           2             3              4              5
    线程1    获得执行权    启动线程2     等待执行权                         获得执行权
       
    线程2                  就绪          获得执行权                      IE                                               获得执行权
      

  11.   

    我认为一开始输出b=1000或者b=2000 都是有可能的   自己也运行了一下  第一次第一行是b=1000  第二次第一行是b=2000.所有楼主您应该多运行几次的  其实应该弄清线程之中有两个状态:可运行状态和运行时状态,当然还有其它状态  呵呵
    任何一个线程需要运行都要经过可运行状态  t.start();  此时一共有两个线程,一个是主线程,一个是刚才创建的线程(暂且称为t吧) 只能说这个线程进入了可运行状态,虽然大多数时候会立即执行里面的run()方法,但是也有可能不会立即执行。至于main线程和t线程哪个先执行  完全是随机的   不能控制的  所以就可能有两个结果了  如果一定要输出第一行是b=1000的话   就需要join()语句了  可以那么改
    TT tt = new TT();
    Thread t = new Thread(tt);
    t.start();
    t.join();
    tt.m2();就变成b=1000了   试一下吧  呵呵
      

  12.   

    2000  b=1000我觉得这样对的  首先运行的是主线程 main 还有个线程要主线程运行1次结束后才有可能运行吧
      

  13.   

    线程执行顺序由Java虚拟机决定
      

  14.   

    任何在你启动新线程之前的操作都是无视synchronized的,启动线程之后每条线程在自己拥有运行权的时间
    跨度是不一样的TT tt = new TT();
    Thread t = new Thread(tt); tt.m2(); //
    System.out.println(tt.b);   //这个时候的打印以及m2方法执行与下面的线程无关t.start(); // 这个时候虽然主线程启动了一条新线程,但是CPU的运行权限未必就给了新线程CPU的运行权还可能还在主线程这里,甚至在其他进程内的线程那里,也就是说与你的程序没关系了我们假设主线程为T1 新线程为T2 第三方的程序例如IE的某条线程我们叫IE,三线程的优先级相等T1启动一直到启动新线程,是和IE在抢CPU执行权限,但是每次拿到权限之后到底能执行多少条语句这个谁也不知道,可能T1执行一条语句后执行权交给IE,也可能是3条语句,但是执行到启动新线程T2的时候
    这时候就成了三国大战了,大家一起抢CPU执行权限同样的三条线程拿到执行权的周期不定,所以每次获得执行权的时候的执行的语句数量也不定
    所以可能是
    T1 执行了1句----》IE执行IE的程序代码1---》T2执行3句---T1 2句而synchronized仅仅只T1和T2才有关系和IE无关,当T1进入synchronized方法后,运行了两句语句尚未将加锁的块执行完所以依然持有锁,但是这个时候计算机可能让T1把执行权限交给T2,T2执行到被synchroized修饰的块的时候就进入了阻塞,这时候可能是把CPU执行权给了T1可能是给了IE,但是肯定要等T1执行完加锁的语句块之后把锁给释放了,等到T2再次拿到CPU执行权限的时候才能进入加锁的块,当然一旦T2进入,T1就不能进入加锁的块了另外T1进入加锁的块之后,T2被阻塞,执行权继续回到T1,T1执行完加锁的块释放掉锁了,但是T2一直没有拿到CPU执行权限,而T1又拿到了并且进入了锁块又执行到一半交出权限到T2手里,T2依然还是只能等待...T2可怜了点-。-另外要学线程的话,sleep,wait,notify,notifyAll,join是必须的
      

  15.   

    在方法前面加Syncrhonized不是个好主意, 这样用是最好的
    int someMethod() {
        ....
        synchronized (this) {
            // some sensitive codes
        }
    }
      

  16.   

    main函数就是一个线程!他先运行!占用cpu!然后Thread完成后 执行你新建的线程!
      

  17.   

    对LZ来说,兄弟们讲得都太深奥了点,建议LZ还是多看看书、多动动手
      

  18.   

    synchronized是同步机制中的,一般是用于防止死锁出现的,对于同一个资源,只允许一个线程访问。你的程序使用了2个不同的资源,它们不会互相影响,输出的结果只是系统将就绪队列中的某个线程调入,然后执行,这是随机调入的。
    26楼的 是synchronized的标准方法,楼主的是简化方式,效果一样。
    同步机制中,消费者,生产者问题比较出名,建议看看。
      

  19.   

    结果是一样的,因为你的t线程要在5S后才会调入内存,main线程比t线程先执行完!
    这2个现场进入就绪队列,并让内存调入是随机的。
      

  20.   

    up,最经典的就是corejava中介绍的银行存取款的例子,相信100个java程序员99个知道
      

  21.   

    这句话有问题   sleep 知识休眠 还占CPU  不是 yield 暂停 让 别的线程执行
    我也晕啦  !
      

  22.   

    sleep和wait的区别去看看
    一个是Thread类的方法,一个是Object的方法
    sleep是休眠没错,但是执行权没有释放
    wait就是完全的释放执行权了sleep(1000); 1秒钟一到立即执行下面的代码
    wait(1000);要重新获得执行权才会执行下面的代码,未必立即执行