文章目录
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
- 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
- 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
修饰一个代码块
一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。
多个线程访问不同对象的时候,执行到这个代码块的时候,均会执行该方法,不会发生方法上锁的情况。
多个线程访问同个对象的一个方法
结果:谁拿到锁谁执行,没拿到的等待
多个线程访问多个对象的一个方法
结果:多个线程各自访问各自拥有对象的方法:
Synchronized 锁一个明确的对象
新建卖票类,sell 方法卖出一张票减一,addTicket 方法补一张票的库存。
新建卖票线程,在 run 方法里面每卖一张票就补一张票的库存。
最后开启五个卖票线程去卖票:
结果:
可以看到这里的并不是 从 A B C D E 依次执行的,因为谁拿到执行权就谁去执行。
没有明确的锁,只是为了给一块代码加上同步
说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码
修饰一个方法
Synchronized 修饰一个方法的格式是
public Synchronized void run(){
// code
}
Synchronized 关键字修饰的是整个函数里面的内容,当相同对象在多线程访问的 run 方法的时候,由于被 Synchronized 关键字修饰,不能同时访问。
这种写法等价于修饰代码块的这种写法:
public void run(){
Synchronized(this){
// code
}
}
两种写法的 code 都是可以互斥访问的。
Synchronized 修饰方法的注意事项:
- Synchronized 修饰方法不能被子类继承
- 接口中不能写 Synchronized
- 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
修饰一个静态的方法
这种情况也是针对于所有对象的这个方法,当多个线程访问这个代码块的时候,均会进行资源竞争的情况,谁拿到锁谁执行,没拿到的等待。
使用方法:
public synchronized static void method() {
// code
}
Synchronized 作用在静态方法中,相当于给整个类加了锁,不管是类的哪个对象访问这个方法的时候,都会竞争锁的资源.
需要注意的是:对其他没加 Synchronized 的方法没有影响。
加上 Synchronized 关键字
结果:
不加 Synchronized
结果:
修饰一个类
这种情况也是针对于所有对象的这个方法,当多个线程访问这个代码块的时候,均会进行资源竞争的情况,谁拿到锁谁执行,没拿到的等待。
使用方法:
class ClassName {
public void method() {
synchronized(ClassName.class) {
// todo
}
}
}
使用 synchronized(ClassName.class)
结果:顺序执行,A 获得锁以后,B 只能等待 A 释放
不使用 synchronized(ClassName.class)
结果:谁竞争到执行权,执行谁
总结:
- 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
- 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
- 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。