一道 java多线程题目,请思考下输出是什么?
public class ThreadTest implements Runnable {
int b=100;
public synchronized void m1() throws Exception{
b=1000;
System.out.println("m1:b="+b);
}
public synchronized void m2()throws Exception{
Thread.sleep(2500);
b=2000;
System.out.println("m2:b="+b);
}
public void run(){
try{
m1();
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ThreadTest tt=new ThreadTest();
Thread t=new Thread (tt);
t.start();
//Thread.sleep(10);
tt.m2();
System.out.println("MAIN,now b="+tt.b);
}
}
=======================================
输出结果是:
m2:b=2000
MAIN,now b=2000
m1:b=1000
我们分析下程序执行过程:
程序开始后,main()线程先开始运行,直到t.start(),另起了一条新线程t, t调用run函数。
多线程是并行的,t.start()并不影响main()线程的继续走下去。这时main()调用了m2()函数,很快m2抢到了tt的锁,并改写了b的值,输出了m2:b=2000.
m1()也加了synchronized, 为什么它没抢到?因为它慢了一步。。。。线程t需要调用run,run再调用m1,它没有main()直接调用m2快。
接下来m2() 运行完以后,CPU去看就绪队列中是否有线程在等待,发现m1,于是就把锁给了m1
但是main()最后面的输出流不需要抢锁,直接read内存中共享的变量b值输出即可。
那么,m1能不能先抢到锁呢?可以,我在测试中发现,极个别也会让m1先抢到锁,继而m1先输出,然后m2,然后system.out.println。
这是一种情况,为了验证,我们再main()函数调用m2()之前,让主线程sleep一下下,就让它睡1ns好了。
public class ThreadTest implements Runnable {
int b=100;
public synchronized void m1() throws Exception{
b=1000;
System.out.println("m1:b="+b);
}
public synchronized void m2()throws Exception{
//Thread.sleep(2500);
b=2000;
System.out.println("m2:b="+b);
}
public void run(){
try{
m1();
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ThreadTest tt=new ThreadTest();
Thread t=new Thread (tt);
t.start();
Thread.sleep(0, 1);;
tt.m2();
System.out.println("MAIN,now b="+tt.b);
}
}
这个时候发现,输出的结果基本上是
m1:b=1000
m2:b=2000
MAIN,now b=2000
道理可能大家都明白了,就是因为sleep延迟了1ns的原因,本来m2可以很轻松抢到锁的,现在让m1轻松先抢到了实例tt的锁,首先锁定了。
值得强调的是,synchronized 同步的是对象,变量是仍然可以访问的。
注意上面代码在不同的计算机上结果可能是不一样的,我的电脑配置 2.6GHz主频,内存8G.