在工作中,遇到多线程的问题,我们可能会添加synchronized关键字,特别方便,主要还是它是java提供的,其内部帮助我们做了很多优化,接下来,我们慢慢分析一波。
众所周知,synchronized包括对象锁和类锁。
第一种:类锁
指synchronize修饰静态的方法或指定锁对象为Class对象,在java中,每个类都有一个class 对象,当我们使用synchronized修饰class对象时或者静态方式时,就相当于锁住了class对象。
public class SynchronizedObjectLock implements Runnable {
static SynchronizedObjectLock instence1 = new SynchronizedObjectLock();
static SynchronizedObjectLock instence2 = new SynchronizedObjectLock();
@Override
public void run() {
method();
}
// synchronized用在静态方法上,默认的锁就是当前所在的Class类,所以无论是哪个线程访问它,需要的锁都只有一把
public static synchronized void method() {
System.out.println("我是线程" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束");
}
public static void main(String[] args) {
Thread t1 = new Thread(instence1);
Thread t2 = new Thread(instence2);
t1.start();
t2.start();
}
}
输出结果:
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束
反编译的结果,可以看到当synchronized修饰在方法中时,无论是静态方法还是普通方法,在flag标志位上有一个ACC_SYNCHRONIZED 标志,其内部也是使用monitor对象进行同步的。
public static synchronized void method();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=0
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #4 // class java/lang/StringBuilder
6: dup
7: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
10: ldc #6 // String 我是线程
12: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: invokestatic #8 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
18: invokevirtual #9 // Method java/lang/Thread.getName:()Ljava/lang/String;
21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: ldc2_w #12 // long 3000l
33: invokestatic #14 // Method java/lang/Thread.sleep:(J)V
36: goto 44
39: astore_0
40: aload_0
41: invokevirtual #16 // Method java/lang/InterruptedException.printStackTrace:()V
44: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
47: new #4 // class java/lang/StringBuilder
50: dup
51: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
54: invokestatic #8 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
57: invokevirtual #9 // Method java/lang/Thread.getName:()Ljava/lang/String;
60: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
63: ldc #17 // String 结束
65: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
68: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
71: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
74: return
第二种:对象锁
使用synchronized修饰普通方法或指定实例对象,当使用synchronized修饰普通方法或者、修饰代码块相当于给当前实例对象加锁。
public class SynchronizedObjectLock implements Runnable {
static SynchronizedObjectLock instence = new SynchronizedObjectLock();
@Override
public void run() {
// 同步代码块形式——锁为this,两个线程使用的锁是一样的,线程1必须要等到线程0释放了该锁后,才能执行
synchronized (this) {
System.out.println("我是线程" + Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "结束");
}
}
public static void main(String[] args) {
Thread t1 = new Thread(instence);
Thread t2 = new Thread(instence);
t1.start();
t2.start();
}
}
输出结果:
我是线程Thread-0
Thread-0结束
我是线程Thread-1
Thread-1结束
下面的反编译结果是synchronized在代码中的实现。我们可以发现在ACC_SYNCHRONIZED这个标志,而是在下面一个monitorenter、两个monitorexit,分别代表着进入对象加锁和正常退出方法和异常退出方法释放锁。
public void run();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=4, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: new #3 // class java/lang/StringBuilder
10: dup
11: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
14: ldc #5 // String 我是线程
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: invokestatic #7 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
22: invokevirtual #8 // Method java/lang/Thread.getName:()Ljava/lang/String;
25: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
34: ldc2_w #11 // long 3000l
37: invokestatic #13 // Method java/lang/Thread.sleep:(J)V
40: goto 48
43: astore_2
44: aload_2
45: invokevirtual #15 // Method java/lang/InterruptedException.printStackTrace:()V
48: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
51: new #3 // class java/lang/StringBuilder
54: dup
55: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
58: invokestatic #7 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
61: invokevirtual #8 // Method java/lang/Thread.getName:()Ljava/lang/String;
64: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
67: ldc #16 // String 结束
69: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
72: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
75: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
78: aload_1
79: monitorexit
80: goto 88
83: astore_3
84: aload_1
85: monitorexit
86: aload_3
87: athrow
88: return