使用
synchronized是Java中的关键字,是一种同步锁。synchronized是内置的语言实现。它修饰的对象有以下几种:
1. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象; 锁是当前实例对象public synchronized void accessVal(int newVal); 复制代码
2. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象; 锁是 Synchronized 括号里的对象
synchronized(syncObject) { //允许访问控制的代码 } 复制代码
其他:
当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:class Foo implements Runnable { // 特殊的instance变量 //零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码: //生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。 private byte[] lock = new byte[0]; Public void methodA() { synchronized(lock) { }} 复制代码
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;锁是当前类的 Class 对象。
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象。原理
通常说的synchronized在方法或块上加锁,这里的锁就是对象锁(当然也可以在类上面),或者叫重量锁,在JVM中又叫对象监视器(Monitor),就是对象来监视线程的互斥。
对象在堆里的逻辑结构: 对象头的结构: 其中Tag的2bit用来显示锁类型。通常我们说synchronized的对象锁,就是这里Tag=10时的monitor对象,这里的Monitor address就是这个monitor对象(就是重量锁)的地址。 加锁的过程很简单:在当前线程的栈帧(stack frame)中生成一个锁记录(lock record),它只是对象头的一个拷贝。然后把对象头里的tag改成00,并把这个栈帧里的lock record地址放入对象头里。若操作成功,那就完成了轻量锁操作。如果不成功,说明有线程在竞争,则需要在当前对象上生成重量锁来进行多线程同步,然后将Tag状态改为10,并生成Monitor对象(重量锁对象),对象头里也会放入Monitor对象的地址。最后将当前线程t排队队列中。当synchronized区域长期都由同一个线程加锁、解锁时,jvm就用偏向锁来做,它的加锁解锁比轻量锁操作起来指令更加简化。不过一旦有其他线程使用synchronized区域,即使没有线程间竞争,也会把偏向锁升级为轻量锁,当然如果发生线程竞争就再升级为对象锁。synchronized与Lock的区别
-
Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
-
synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
-
Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
-
通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
-
Lock可以提高多个线程进行读操作的效率。
-
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。