一、synchronized简介
1、synchronized关键字的作用
首先看看官方文档对其的说明:同步方法支持一种简单的策略来防止线程干扰和内存的一致性错误,如果一个对象对多个线程可见,则该对象变量的所有读取或写入都是通过同步方法完成的。
此种解释比较难懂,我们用一句话概括出synchronized关键字的作用:能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
2、synchronized关键字的地位
-
synchronized是Java的关键字,被Java语言原生支持。
-
是最基本的互斥手段。
-
是并发编程中的元老级角色,是并发编程必学内容。
3、不使用并发的后果
首先我们看一段代码:
public class SynTest implements Runnable{
public static SynTest instance = new SynTest();
public static int i = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join(); //使t1和t2执行完之后才输出 i
t2.join();
System.out.println(i);
}
@Override
public void run() {
for(int j=0; j<100000; j++) {
i++;
}
}
}
在这段代码中,i 的值最后总是小于200000。
4、synchronized的两种用法
4.1 第一种方法:对象锁
包括方法锁(默认锁对象为this当前实例对象)和同步代码块锁(自己指定锁对象)。
4.2 第二种方法:类锁
指synchronized修饰静态方法或指定锁为Class对象。
5、多线程访问同步方法的七种情况
- 两个线程同时访问一个对象的同步方法。
- 两个线程同时访问两个对象的同步方法。
- 两个线程访问的是synchronized修饰的静态方法。
- 同时访问同步方法和非同步方法。
- 访问同一个对象的不同普通同步方法。
- 同时访问静态synchronized和非静态synchronized方法。
- 当方法抛出异常时,会释放锁。(扩展:ock类抛出异常不会释放锁)
二、synchronized的性质和原理
1、可重入性质
1.1 什么是可重入:可重入指的是统一线程的外层函数获得锁之后,内层函数可以直接获取该锁,也叫递归锁。避免死锁、提升封装性。
1.2 可重入的好处:避免死锁,比如说如果没有可重入,方法A包含方法B,方法A和方法B都需要同一把锁,那么在方法A中执行到方法B时,方法B要竞争获得锁,但方法A没有执行完毕不会释放锁,这就造成了死锁。提升封装性,避免了不断地加锁、开锁,提升了封装性。
2、不可中断性质
不可中断指的是:一旦这个锁已经被别人获得了,如果我还想获得,我只能选择等待或者阻塞,直到别的线程释放这个锁。如果别人永远不释放锁,那么我只能永远地等待下去。
扩展:Lock类可以拥有中断的能力。如果我觉得等的时间太长了,我有权利中断现在已经获取到锁的线程执行; 如果我觉得等的时间太长不想再继续等了,也可以选择退出。
3、synchronized原理
可重入原理:加锁计数器
可见性原理
三、synchronized的缺点
- 效率低:锁的释放情况少、视图获得锁的时候不能设置超时、不能中断一个正在试图获得锁的进程。
- 不够灵活:加锁和释放的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的。
- 无法知道是否成功的获得锁。
四、常见面试问题
1、synchronized使用的注意点:
锁对象不能为空,作用域不宜过大,避免死锁。
2、如何选择Lock和synchronized?
如果可以尽量使用JUC包中的类,此外尽量使用synchronized,需要使用Lock中的特性时就去使用Lock。
、