java锁机制初探,实例学习
package com.yjf.image;
public class TT implements Runnable {
int a = 0;
@Override
public void run() {
try {
m1();
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void m1() throws InterruptedException {
a = 1000;
Thread.sleep(5000);
System.out.println("m1:" + a);
}
public synchronized void m2() throws InterruptedException {
Thread.sleep(1000);
a = 2000;
System.out.println("m2:" + a);
}
public static void main(String[] args) throws InterruptedException {
TT tt = new TT();
Thread t = new Thread(tt);
t.start();
Thread.sleep(20);
tt.m2();
Thread.sleep(6000);
System.out.println("main:" + tt.a);
}
}
定义了一个线程类TT,实现Runnable接口,两个方法m1和m2都加了锁,main方法有两个线程:线程main和线程t。运行结果为:
m1:1000
m2:2000
main:2000
因为m1和m2都加了锁,代码先执行了t.start,即执行到了m1代码块,此时线程睡了5秒,但它睡着的时候还抱着当前对象的锁。在它睡着的过程中,代码执行到tt.m2();,但由于m2是个synchronised的方法,它要执行的话,必须要先获得一把锁,但这把锁被m1抱着,所以必须等到m1执行完成后,m2才能执行。
如果把m2的synchronized修饰符去掉,那么运行结果为:
m2:2000
m1:2000
main:2000
因为m1睡着了,抱着当前对象的锁,但m2不是一个synchronized方法,它执行前不用获得锁,所以在m1睡着的过程中,m2先执行完了。
如果把m1声明为public static synchronized void m1(),把m2声明为synchronized,把成员变量a变成static,运行结果为:
m2:2000
m1:2000
main:2000
分析:因为m1是static方法,它睡着的时候抱着一把锁,但这把锁不是当前对象的锁,而是当前类的锁。m2执行时,需要获得一把当前对象的锁,它当然可以获得。所以m2比m1先执行完。
如果把m2声明为public static synchronized void m2(),把m1声明为public static synchronized void m1(),把tt.m2()改为TT.m2(),执行结果为:
m1:1000
m2:2000
main:2000
分析:m1睡着的时候锁住了当前类,而m2执行的时候需要获得当前类的锁,所以必须等m1执行完把锁释放后,m2才能执行。
最后提一点,main方法中有这几句话:
t.start();
Thread.sleep(20);
TT.m2();
如果去掉中间这句:Thread.sleep(20);,会出现不一样的结果。因为主线程的优先级大于分支线程,CPU会这样分配时间。t.start(),启动一个线程,实际上不是启动,而是初始化了一个线程t,CPU会先分配时间片给TT.m2(),所以我加个Thread.sleep(20),让主线程睡一会,排除干扰。