目录
一、特性
1、互斥性
2、不可中断性
3、可重入性
二、使用
1、修饰普通方法
2、修饰静态方法
3、修饰代码块
三、锁机制
一、特性
1、互斥性
- 当线程进入synchronized修饰的代码块时,就相当于加锁。
- 当线程退出synchronized修饰的代码块时,就相当于解锁。
在同一时刻只允许一个线程持有某个对象的锁,此时只有一个线程使用该代码块。通过这种特性实现了多线程的协调机制。因此互斥性也成为了操作的原子性。
当已经有线程获取到该对象的锁时,其他线程也执行到该对象的被synchronized所修饰的方法也想获取锁进行加锁操作。但由于互斥性无法获取该锁,此时其他线程就会进入阻塞等待状态,直到之前的线程解锁后,其他线程才有机会获得该锁。
注:synchronized是非公平锁,并不会遵守先来后到的规则,而是同时竞争。是否能获得锁还得看操作系统的调度
2、不可中断性
当线程执行到的对象的锁被其他线程获得后,如果当前线程还想获得该锁,就只能进行阻塞等待,直到已获得该锁的线程释放锁后再尝试去获得锁。
3、可重入性
当一个线程请求另一个线程持有的锁时,请求的线程会阻塞,这是synchronized的不可中断性。但当线程去获取自己所拥有的锁时会请求成功而不会阻塞,这就是锁的可重入性。
重入的原理:每个锁关联一个计数器和持有者线程,当计数器为0时候,这个锁被认为是没有被任何线程持有。
- 当有线程持有锁时计数器进行自增并且记下锁的持有线程,当同一线程继续获取锁时计数器继续自增。
- 当线程退出代码块时相应地计数器减1,直到计数器为0时锁被释放。此时该锁才能被其他线程获得。
同一线程的外层方法获得锁之后,内层方法可以直接获取改锁,可以避免内外层死锁(如下图)。
Object locker=new Object();synchronized(locker){synchronized(locker){}
}
synchronized是可重入锁,可以防止出现死锁
当线程进入了外层的 synchronized 方法时会拿到该锁,加上锁后将会执行代码。而内部又有一个 synchronized修饰的方法,此时也需要获取到同一把锁。想要获取之前的锁就必须要让外层的方法执行完后释放锁,但外层 synchronized 包裹的代码执行完则需要内部的代码先执行完才行。因此该方法无法继续执行,产生了死锁。
此时synchronized 是可重入锁就很好的解决了死锁的问题。
二、使用
1、修饰普通方法
使用synchronized修饰普通方法时,其作用域是整个方法,锁住的对象仅仅是当前对象。
synchronized void func1(){}
2、修饰静态方法
使用synchronized修饰静态方法时,其作用域时整个静态方法,因为静态方法属于类而不是对象,因此锁住的对象时当前类对象。
synchronized static void func2() {}
3、修饰代码块
使用synchronize修饰代码块时,其作用域是整个代码块,作用对象是括号中的对象,这个作用对象可以是指定的对象,也可以是类。
Object locker=new Object();synchronized (locker) {}
- 在 Java 中,任何一个继承自 Object 类的对象,都可以作为锁对象。加锁操作实际上是在操作 Object 对象头中的一个标识位。
- 如果括号中的是this,说明锁的对象就是当前对象。
- 如果括号中的是 xxx.class,说明锁的对象就是类对象。
三、锁机制
synchronized:
- 既是乐观锁,也是悲观锁。
- 既是轻量级锁,也是重量级锁。
- 轻量级锁基于自旋实现,重量级锁基于挂起等待实现。
- 不是读写锁。
- 是可重入锁。
- 是非公平锁。