先来看一段代码
public class MultiThread {
private static int num = 0;
/** static */
public synchronized void printNum(String tag){
try {
if(tag.equals("a")){
num = 200;
System.out.println("tag a, set num over!");
Thread.sleep(300);
} else {
num = 500;
System.out.println("tag b, set num over!");
//Thread.sleep(300);
}
System.out.println("tag " + tag + ", num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//注意观察run方法输出顺序
public static void main(String[] args) {
//俩个不同的对象
final MultiThread m1 = new MultiThread();
final MultiThread m2 = new MultiThread();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
m1.printNum("a");
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
m2.printNum("b");
}
});
t1.start();
t2.start();
}
}
运行之后得到的结果由随机性:
可能是
tag b, set num over!
tag a, set num over!
tag b, num = 200
tag a, num = 200
也可能是
tag a, set num over!
tag b, set num over!
tag b, num = 500
tag a, num = 500
当然,还有其他的可能性,这里就不一一列举了。
可以看到,synchronized没起作用。这是因为synchronized锁住的是对象,线程t1,t2分别获取的是m1,m2的锁,他们并没有在获取锁上有竞争关系,因此,出现非同步的结果。
那如果要让printNum方法在多个对象之间也是线程安全的呢,一个对象在执行这个方法时,其他对象必须阻塞等待,一次只能有一个对象能执行这个方法。只需要加个static关键字来修饰printNum方法,这时synchronized关键字锁住的就是MultiThread的.class类了。
将printNum方法修改如下:
public static synchronized void printNum(String tag)
...
结果如下:
tag b, set num over!
tag b, num = 500
tag a, set num over!
tag a, num = 200
或者:
tag a, set num over!
tag a, num = 200
tag b, set num over!
tag b, num = 500
这时的输出结果就符合我们的预期了。
最后,总结一下:
synchronized的使用情况大概就是下面几种:
- synchronized修饰非静态方法
- synchronized修饰静态方法
- 同步代码块的synchronized (this)
- 同步代码块的synchronized (非this对象)
- 同步代码块的synchronized (类.class)
synchronized修饰非静态方法、同步代码块的synchronized (this)用法和synchronized (非this对象)的用法锁的是对象,线程想要执行对应同步代码,需要获得对象锁。
synchronized修饰静态方法以及同步代码块的synchronized (类.class)用法锁的是类,线程想要执行对应同步代码,需要获得类锁。