synchronized 的使用
实例级别
public class UtilInstance {
private int num;
public synchronized void pp() {//这是实例级别的
num++;
System.out.println("pp " + num);
}
}
public class ContentInstance {
public static void main(String[] args) {
final CountDownLatch latch=new CountDownLatch(1);
final UtilInstance aaa = new UtilInstance();
for(int i=0;i<10;i++){
new Thread(new Runnable() {
public void run() {
try {
latch.await();
aaa.pp();//测试实例级别
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
latch.await();
aaa.pp();
//测试实例级别
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
latch.countDown();
}
}
}
output
pp 1
pp 2
pp 3
pp 4
pp 5
pp 6
pp 7
pp 8
pp 9
pp 10
pp 11
pp 12
pp 13
pp 14
pp 15
pp 16
pp 17
pp 18
pp 19
pp 20
我们可以看到,synchronized放在普通方法上,他的锁是实例级别,两个线程调用同一个实例的这个方法,就能实现同步执行
后面我思考了一下,既然是实例锁,那么假如我的这个类里面有另外一个sync方法,然后我在调用第一个sync方法的时候,再调用第二个sync方法,会等待第一个sync方法执行完才执行第二个sync方法吗,然后我测试了一下,在原有代码上加了一个sync 方法
public class UtilInstance {
private int num;
......
public synchronized void ss() {//这是实例级别的
num++;
System.out.println("ss -->" + num);
}
}
new Thread(new Runnable() {
public void run() {
try {
latch.await();
aaa.ss();//测试实例级别
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
latch.await();
aaa.pp();
//测试实例级别
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
latch.countDown();
output:
ss -->1
ss -->2
休眠五秒
pp 3
ss -->4
休眠五秒
pp 5
ss -->6
休眠五秒
pp 7
哈哈,真相大白了,同一个实例,多线程调用里面的sync方法,会等待上一个sync方法执行完后释放实例对象锁,然后下一个sync方法获得实例对象锁,然后调用
静态方法级别
public class UtilStaticMethod {
private static int num;
public static synchronized void sss(){
num++;
System.out.println(num);
}
}
public class ContentStaticMethod {
public static void main(String[] args) {
final CountDownLatch countDownLatch=new CountDownLatch(1);
for(int i=0;i<1000;i++){
new Thread(new Runnable() {
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
UtilStaticMethod.sss();
}
}).start();
}
countDownLatch.countDown();
}
}
546
547
548
549
550
551
552
553
554
555
我们可以看到,这个也做到了多线程之间调用方法的同步,这里也是对象锁,锁定的是当前的class
为了验证sync static 方法锁的是class,我们加一个方法来验证下
public static synchronized void syncMethod(){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num++;
System.out.println(num+" syncMethod,休眠5秒后");
}
public static void syncClass(){
synchronized (UtilStaticMethod.class){
num++;
System.out.println(num+" syncClass");
}
}
final CountDownLatch countDownLatch=new CountDownLatch(1);
for(int i=0;i<1000;i++){
new Thread(new Runnable() {
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
UtilStaticMethod.syncMethod();
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
UtilStaticMethod.syncClass();
}
}).start();
}
countDownLatch.countDown();
output:
43 syncMethod,休眠5秒后
44 syncMethod,休眠5秒后
45 syncClass
46 syncClass
47 syncMethod,休眠5秒后
48 syncClass
49 syncMethod,休眠5秒后
50 syncClass
51 syncMethod,休眠5秒后
52 syncClass
我们可以看到,正如我们猜想的那样,sync static 方法,锁的是当前class,也是对象锁
代码块级锁
public void test(){
private Object lock=new Object();
synchronized(lock){
dothing();
}
}
这种也是对象锁
简单分析
接下来我们看看这个sync(对象)到底做了什么
javap -c UtilStaticMethod
public static void syncClass();
Code:
0: ldc #16 // class sync/UtilStaticMethod
2: dup
3: astore_0
4: monitorenter
5: getstatic #7 // Field num:I
8: iconst_1
9: iadd
10: putstatic #7 // Field num:I
13: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
16: new #9 // class java/lang/StringBuilder
19: dup
20: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
23: getstatic #7 // Field num:I
26: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
29: ldc #17 // String syncClass
31: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: invokevirtual #14 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: aload_0
41: monitorexit
42: goto 50
45: astore_1
46: aload_0
47: monitorexit
48: aload_1
49: athrow
50: return
Exception table:
from to target type
5 42 45 any
45 48 45 any
}
这里的字节码对应这里的java代码
public static void syncClass(){
synchronized (UtilStaticMethod.class){
num++;
System.out.println(num+" syncClass");
}
}
我们慢慢分析到底做了什么
首先,ldc 命令是把一个值从常量池推到操作栈顶, 后面跟了个序号 #16,我们来看一下
#16 = Class #49 // sync/UtilStaticMethod
我们在来看一下 #49
#49 = Utf8 sync/UtilStaticMethod
这里#16是保存了一个类全名叫sync.UtilStaticMethod的类,这里就是我们的锁对象
因为操作栈是临时的,所以这里dup复制了一份并放到栈顶,然后astore_0讲操作栈顶的数据放到本地变量
然后就是monitorenter,使用那个锁对象(我猜测是使用刚才复制的那个存放到本地变量表里的对象,不过看了源码分析,看调的方法是从栈里面获取锁对象,然后调用锁方法的),后面执行这个方法的时候会判断是否有锁没解,再执行或者等待放到等待队列(不过这里的jvm源码我没找到)
然后就是获得定义的num字段,自增,然后StringBuilder 进行append数据,然后调用打印方法
最后就是monitorexit,释放那个锁对象