1. synchronized锁(悲观锁、同步锁)
-
synchronized关键字:表示“同步”的。它可以对“多行代码”进行“同步”——将多行代码当成是一个完整的整体,一个线程如果进入到这个代码块中,会全部执行完毕,执行结束后,其它线程才会执行。这样可以保证这多行的代码作为完整的整体,被一个线程完整的执行完毕。
-
synchronized被称为“重量级的锁”方式,也是“悲观锁”——效率比较低。
1.1synchronized有几种使用方式:
- a).同步代码块【常用】
- b).同步方法【常用】
a.同步代码块:
格式:
synchronized(锁对象){
}
锁对象:
1.语法上,可以是任意类的对象
2.如果多条线程想要实现同步,那么这多条线程的锁对象必须一致
b.同步方法:
格式:
修饰符 synchronized 返回值类型 方法名(形参列表){ }
锁对象:
非静态同步方法: 锁对象this
静态同步方法:该方法所在类的字节码对象(类名.class)
2.Lock锁
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
-
public void lock()
:加同步锁。 -
public void unlock()
:释放同步锁。
面试题: sychronized和ReenteredLock区别?
① 底层实现上来说
synchronized 是JVM层面的锁,是Java关键字,通过monitor对象来完成(monitorenter与monitorexit),对象只有在同步块或同步方法中才能调用wait/notify方法,ReentrantLock 是从jdk1.5以来(java.util.concurrent.locks.Lock)提供的API层面的锁。
synchronized 的实现涉及到锁的升级,具体为无锁、偏向锁、自旋锁、向OS申请重量级锁,
ReentrantLock 实现则是通过利用CAS(CompareAndSwap)自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能。
② 是否可手动释放
synchronized 不需要用户去手动释放锁,synchronized 代码执行完后系统会自动让线程释放对锁的占用; ReentrantLock则需要用户去手动释放锁,如果没有手动释放锁,就可能导致死锁现象。一般通过lock()和unlock()方法配合try/finally语句块来完成,使用释放更加灵活。
③ 是否可中断
synchronized是不可中断类型的锁,除非加锁的代码中出现异常或正常执行完成; ReentrantLock则可以中断,可通过trylock(long timeout,TimeUnit unit)设置超时方法或者将lockInterruptibly()放到代码块中,调用interrupt方法进行中断。
④ 是否公平锁
synchronized为非公平锁 ReentrantLock则即可以选公平锁也可以选非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁。
3. volatile关键字
volatile是一个"变量修饰符",它只能修饰"成员变量",它能强制线程每次从主内存获取值,并能保证此变量不会被编译器优化。
-
volatile能解决变量的可见性、有序性;
-
volatile不能解决变量的原子性
面试题 :能够说明volatile关键字和synchronized关键字的区别?
- volatile只能修饰成员变量,synchronized可以修饰代码块或者方法
- volatile是强制要求子线程每次使用共享变量都是重新从主内存中获取, synchronized实现的是互斥访问
- volatile只能解决可见性,有序性问题,不能解决原子性问题,但synchronize都可以解决
3.死锁是什么?遇到死锁问该怎么解决?
死锁:多个线程同时被阻塞,它们中的⼀个或者全部都在等待某个资源被释放。由于线程被⽆限期地阻塞,因此程序不可能正常终⽌。
(1)产生死锁的4个必要条件:
- 互斥条件:所谓互斥就是线程在某一个时间内独占资源
- 请求与保持条件:⼀个线程因请求资源⽽阻塞时,对已获得的资源保持不放。
- 不剥夺条件:线程已获得资源,在未使用完之前,不能强行剥夺。
- 循环等待条件:若⼲线程之间形成⼀种头尾相接的循环等待资源关系。
(2)如何避免线程死锁?
产⽣死锁的四个必要条件,为了避免死锁,我们只要破坏产⽣死锁的四个条件中的其中⼀个就可以了。
- 破坏互斥条件 :这个条件我们没有办法破坏,因为我们⽤锁本来就是想让他们互斥的(临界资源需要互斥访问)。
- 破坏请求与保持条件 :⼀次性申请所有的资源。
- 破坏不剥夺条件 :占⽤部分资源的线程进⼀步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
- 破坏循环等待条件 :靠按序申请资源来预防。按某⼀顺序申请资源,释放资源则反序释放。破坏循环等待条件。
避免死锁最简单的方法就是 阻止循环等待条件 ,将系统中所有的资源设置标志位、排序,
规定所有的进程申请资源必须以一定的顺序(升序或降序) 做操作来避免死锁。