注意
synchronized 不能锁String、Integer、Long类型。
synchronized (this) 锁的是当前类的对象。 this关键字指向的是当前对象的引用。
synchronized (this.getClass()) 锁的是当前类的class对象。
synchronized 不能防止出现指令重排序的情况。
使用
// 一个有若干个synchronized方法的对象
class MyClass {
public synchronized void hello() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello");
}
public synchronized void world() {
System.out.println("world");
}
}
class Test {
public static void main(String[] args) {
MyClass myClass = new MyClass();
new Thread(()-> {
myClass.hello();
}).start();
new Thread(()-> {
myClass.world();
}).start();
// 输出
// hello
// world
}
}
如果一个对象(这里指 myClass ),它里面有若干个synchronized方法,
当有多个线程对其访问时,在某一时刻,只能有唯一的一个线程进入,其中某个synchronized方法。其他的线程
即使访问的是其他的synchronized方法,也要等待。
原因:线程若要进入这个对象(例:myClass),执行某个方法。需要先获取到当前对象(类)的锁(即myClass的锁)。
而一个类,只有一个锁,所以一次只能有一个线程可以进入。
当某一个线程去访问 static synchronized 方法时,他获取到的锁就不是当前对象(例 myClass )的锁了,而是
当前对象所对应的class对象的锁。
public static synchronized void method2() { }
因为,静态方法并不属于当前对象,它属于当前对象所对应的class对象。
例:
MyClass myClass1 = new MyClass();
MyClass myClass2 = new MyClass();
通过new关键字,一个类可以有多个对象(一个对象一把锁),但一个类对应的class对象,只有一个(一把锁)。
synchronized 是可重入锁,当某个线程获取到了对象(例:myClass )的锁,因为是同一个线程、同一把锁,所以
它也可以访问myClass 对象中其他的 synchronized方法。从而避免死锁。
例:
class MyClass {
public synchronized void hello() {
System.out.println("hello");
world();
}
public synchronized void world() {
System.out.println("world");
}
}
问
一问
public class Test {
public synchronized void method1() {
}
public void method2() {
}
}
Test test =new Test();
问:在实例化一个对象 test后,假设线程A在执行method1方法,此时线程B可不可以执行同一个对象的
method2方法?
二问
public class Test {
public synchronized void method1() {
}
public synchronized void method2() {
}
}
Test test =new Test();
问:在实例化一个对象 test后,假设线程A在执行method1方法,此时线程B可不可以执行同一个对象的
method2方法?
三问
public class Test {
public synchronized void method1() {
}
public static synchronized void method2() {
}
}
Test test =new Test();
问:在实例化一个对象 test后,假设线程A在执行method1方法,此时线程B可不可以执行同一个对象的
method2静态方法?
答
一问
可以,因为synchronized 方法,和非synchronized 方法可以同时运行。
二问
不可以,因为对于同一个对象来说,它所有的synchronized 方法,锁(monitor)的都是同一个东西( test )。
三问
可以,因为 method1() 方法的 synchronized 关键字,锁的是当前的对象(类:Test),
method2() 方法的 synchronized 关键字,锁的是当前的Test对象,所对应的class对象。
底层原理
-
当我们使用synchronized 关键字修饰代码块时,字节码层面上是通过monitorenter 与
monitorexit 指令来实现锁的获取与释放动作。
-
当线程进入到monitorenter 指令后,线程会持有被同步对象(例:括号内的object)
的Monitor对象,执行monitorexit 退出指令后,线程会释放Monitor对象。 -
对于synchronized关键字修饰方法来说,并没有出现 monitorenter 与monitorexit 指令,
而是出现了 ACC_SYNCHRONIZED标志。
原因:JVM使用ACC_SYNCHRONIZED访问标志区分一个方法是否为同步方法;当方法被调用时,调用指令会检查该方法是否拥有ACC_SYNCHRONIZED标志。如果有,那么执行线程将会现持有方法所在对象的Monitor对象,然后在执行方法体;在该方法执行期间,其他任何线程均无法在获取到这个Monitor对象。当线程执行完该方法后(即使是出现了异常),它会释放掉Monitor对象,
-
原则上来说,这个静态方法method,并不属于MyTest3这个类。因为,我们可以在
MyTest3这个类,并没有生成任何对象的情况下,通过MyTest3这个类名,直接调用
method()静态方法。 因为静态方法,不能使用this关键字,所以args_size = 0。
到JDK1.6,对synchronized加入了很多优化措施,如:自适应自旋、锁消除、锁粗化、偏向锁、轻量级锁等。
注:资料主要来源【张龙】