因为程序中多线程场景的存在,所以需要采用synchronized关键字赖规避并发问题.
synchronized关键字能作用的范围有类,方法,静态方法,代码块.
1.修饰类:作用的对象就是这个类的所有对象.
class ClassName {
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
2.修饰方法:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用该方法的那个对象.由此可见,作用到方法的同步关键字比作用到类的同步关键字的力度要小.但是同步关键字不能修饰接口方法,同样也不修饰构造方法,同步关键字也不能被继承.
当线程在访问某个对象的一个同步方法时,那么其他线程不能访问该对象的其他同步线程.一个对象实例,只能加持一个锁.
public synchronized void method()
{
// todo
}
3.修饰静态方法:作用的范围是整个方法,作用的对象是该类的所有对象.总所周知,静态方法是属于类的,所以同步关键字是锁定的这个类的所有对象.
public synchronized static void method() {
// todo
}
4.修饰同步代码块:作用范围是{}包括的那部分代码,作用的对象是调用这块代码的对象.
- 当一个线程访问一个对象的一个synchronized(this)同步代码块时,其他线程对object中的其他synchronized代码块也不能访问.
Synchronized同步代码块底层原理
创建一个synchronized代码demo:
/**
* Created by xueyunlong on 17-9-12.
*
*/
public class SynchronizedDemo {
public void test(){
synchronized (this){
System.out.println("同步代码块");
}
}
}
对上述类先进行编译,再进行反编译.
javac -g SynchronizedDemo.java
javap -c SynchronizedDemo.class
得到反编译结果为:
Compiled from "SynchronizedDemo.java"
public class SynchronizedDemo {
public SynchronizedDemo();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void test();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String 同步代码块
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2
18: aload_1
19: monitorexit
20: aload_2
21: athrow
22: return
Exception table:
from to target type
4 14 17 any
17 20 17 any
}
其中,同步关键字的实现是通过monitorenter和monitorexit实现的.其大致原理如下:
1.每个对象都一个monitor监视器锁,每次请求该代码块的时候即执行monitorenter时,都会先判断monitor的值是否大于1,大于1说明有线程占用,阻塞.
2.执行monitorexit,monitor的进入数减1,当进入数等0时,线程退出monitor.
Synchronized同步方法底层原理
public class com.zejian.concurrencys.SyncMethod
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool;
//省略没必要的字节码
//==================syncTask方法======================
public synchronized void syncTask();
descriptor: ()V
//方法标识ACC_PUBLIC代表public修饰,ACC_SYNCHRONIZED指明该方法为同步方法
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=3, locals=1, args_size=1
0: aload_0
1: dup
2: getfield #2 // Field i:I
5: iconst_1
6: iadd
7: putfield #2 // Field i:I
10: return
LineNumberTable:
line 12: 0
line 13: 10
}
SourceFile: "SyncMethod.java"
1.在同步方法的实现中,并没有像同步代码块中的没有monitorenter指令和monitorexit指令,而是通过ACC_SYNCHRONIZED访问标志来标识一个方法的同步性.
synchromized与ReentrantLock的对比
JDK1.6之后,虚拟机对Synchronized关键字进行了整体优化,在整体性能上两者基本上没有太大的差距.所以我们需要选取的两者的时候,就要依据当前的场景了.synchronized关键字更加清晰简洁,而ReentrantLock提供了多样化的同步特性,比如超时获取锁,可以终端的获取锁,等待唤醒机制的多个条件变量等等.