对锁的理解
- 由于在多线程环境下对共享变量进行修改会产生数据不一致的问题,所以就需要需要用到锁来保证线程安全(做同步)(同步:一个线程在对共享变量进行操作时,其他线程都不可以对这个共享变量进行操作,直到该线程完成操作)。
- 而同步的本质是通过锁来实现的。锁是一种抽象的概念,实际上它是一个标记。为了实现每次只能有一个线程进入代码块,那么需要在某个地方做个标记,这个标记必须每个线程都能看到,当标记不存在时可以设置该标记,其余后续线程发现已经有标记了则等待拥有标记的线程结束同步代码块取消标记后再去尝试设置标记。这个标记可以理解为锁。
- 实现锁的方式也有很多种。例如synchronize 是在对象头设置标记,Lock 接口的实现类基本上都只是某一个 volitile 修饰的 int 型变量其保证每个线程都能拥有对该 int 的可见性和原子修改。
什么叫线程安全, 如何保证线程安全(互斥同步,非阻塞同步)
- 在多线程环境下也不会出现数据不一致就叫线程安全
- 互斥同步(synchronized 和 ReentrantLock)
- 非阻塞同步(乐观锁、CAS)
临界区是指什么?
临界区是指访问公共资源的代码片段,这些公共资源无法被多个线程同时访问,一个只能有一个线程访问它。
关键字synchronized
synchronized是一种重量级锁,但是随着java1.6开始对其进行了各种优化之后,synchronized的性能已经大大提升了。
1. synchronized作用和用法?
- synchronized可以实现线程间的同步,使用synchronized对代码块加锁,可以使每次只能有一个线程进入代码块,从而保证线程的安全。synchronized可以作用在方法上和代码块上。
- ①指定加锁对象:对指定对象加锁
②直接作用于实例方法:对当前实例加锁
③直接作用于静态方法,对当前类加锁。
2. synchronized(this)和synchronized(Xx.class)区别
- synchronized(this)是对当前实例加锁,如果有多个实例就有相对应的多个锁
- synchronized(类的名.class)是对当前类加锁,不管有几个对象就公用一把锁
3. synchronized作用在方法上和代码块区别?
- synchronized作用在方法上默认使用this或者当前类做为锁,作用在静态方法是对当前类加锁,作用在实例方法上是对当前实例加锁。而作用在代码块上需要手动指定锁对象
- synchronized作用在代码块上比作用在方法上锁的范围要小,性能要好一些。
4. synchronized与Lock的区别
- 1.Lock是一个接口,synchronized是java内置关键字;
- 2.通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
- 3.Lock可以让等待锁的线程响应中断,而使用synchronized时,等待的线程不能够响应中断;
- 4.synchronized会自动释放锁,Lock需在finally中手动释放锁,否则可能造成死锁。
- 5.Lock可以提高多个线程进行读操作的效率。
- 6.synchronized是非公平锁,Lock可以选择是否公平。
5. synchronized 和 ReentrantLock区别
- 1.synchronized通过作用在通过代码块或者同步方法上来使用,ReentrantLock是通过创建ReentrantLock对象,然后调用这个对象的方法来使用。
- 2.synchronized会自动释放锁,Lock需在finally中手动释放锁。
- 3.使用synchronized等待的线程不能响应中断,使用ReentrantLock等待的线程可以响应中断。
- 4.synchronized是非公平锁,ReentrantLock可以选择是否公平。
- 5.ReentrantLock可以通过Condition可以绑定多个条件,synchronized只有一个条件
- 6.(原理上)synchronized是JVM级别的,ReentrantLock是API级别的
- 7.(原理上)底层实现不一样, synchronized 是基于 Java 对象头和 Monitor 机制来实现的,ReenTrantLock是基于AQS来实现的。
6. synchronized底层实现原理(写的不好)
线程通过获取对象的moniter所有权来获取锁的,当一个线程碰到了synchronized关键字时,首先需要获取当前加锁对象的moniter监视器,如果获取成功,就可以继续执行,如果失败就会等待。
7. synchronized锁升级(1.8的改变,锁的升级过程)
- 当线程首次获取锁时,会在对象头中的锁记录里存储当前线程id,表示锁偏向当前线程,这就是偏向锁。
- 当另一个线程需要获取锁时,会直接比较锁对象头中的锁记录中储存的线程id,如果线程id相同,表示是当前线程,则直接进入临界区,此时仍然是偏向锁。如果线程id不相同,则偏向锁取消,切换到轻量级锁,则线程通过几次CAS自旋直接修改锁记录为当前线程id,如果成功,则获取到轻量级锁,进入临界区,如果自旋一定次数后没有获取到锁,锁升级为重量级锁。
- 锁膨胀为重量级锁后,试图获取锁的线程会进入阻塞状态,持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程重新竞争锁。
更好的说法:
- 偏向锁是指一段同一代码块一直被同一个线程访问,那么该线程会自动获取锁,降低获取锁的代价。
- 轻量级锁是指锁是偏向锁时,被另一个线程访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式获取锁,不会阻塞,提高性能。
- 重量级锁是指,锁是轻量级锁时,另一个线程虽然是自旋,但是自旋并不会持续下去,当自旋到一定次数还没有获取到锁,就会进入阻塞态,该锁升级为重量级锁,重量级锁会让其他试图获取锁的线程会进入阻塞状态,持有锁的线程释放锁之后会唤醒这些线程,被唤醒的线程重新竞争锁。