Java知识点学习(第3天)

Sychronized和ReentrantLock有哪些不同点?

  1. 本身属性
    Sychronized是Java中的一个关键字,底层是JVM,也就是通过C++进行实现的;ReentrantLock是JDK提供的一个类,我们需要生成ReentrantLock的对象,然后调用它lock/unlock,trylock方法去加锁解锁。

  2. 锁本身
    Sychronized(JVM层面)会自动的进行加锁和解锁;ReentrantLock(API层面)需要去调方法,手动的去加锁或解锁。

  3. 锁的属性
    Sychronized只可以是非公平锁,ReentrantLock可以是公平锁也可以是非公平锁。

  4. 锁的方式
    Sychronized锁的是对象,锁信息保存在对象头中;ReentrantLock通过int类型的state来表明当前的锁有没有被其他占用。

  5. 锁升级
    Sychronized底层有锁的升级过程,偏向锁到轻量级锁到重量级锁;ReentrantLock是没有锁升级过程的。

ThreadLocal有哪些应用场景?它的底层是如何实现的?

ThreadLocal是Java提供的线程本地存储缓存的一种机制,可以利用该机制将数据缓存在线程内部,该线程在任何时间、任何方法中都可以获取到缓存的数据。

ThreadLocal的底层是通过ThreadLocalMap实现的,每个Thread对象中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为缓存的值。

ThreadLocal会造成内存泄漏,因为在ThreadLocal对象使用完之后,应该要把设置的key,value进行清空回收,但线程池中的线程不会回收,而线程对象是强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向entry对象,因此线程不回收,对应的entry对象也不会回收。举个例子,如果线程池创建了一个线程a,线程a直接在ThreadLocalMap中进行entry的缓存存储,在存储结束后,线程a被调用去实现其他的功能,那么ThreadLocalMap中entry对象就是内存泄漏,但是entry也不会消失,因为线程a还存活着,但是entry对象已经失去了使用价值。因此这使用完ThreadLocalMap的缓存之后,应该调用ThreadLocal对象的remove()方法去清除对应的缓存。

ReentrantLock分为公平锁和非公平锁,那底层分别是如何实现的?

首先无论是公平锁还是非公平锁,它们的底层都是使用AQS来进行排队的,它们的区别在于线程在使用lock()方法加锁时:

  1. 如果是公平锁,会先检查AQS队列中是否有线程在排队,如果有线程在排队,则当前线程也排队。

  2. 如果是非公平锁,则不会去检查是否有线程在排队,而是直接去竞争锁。

总之,无论是公平锁还是非公平锁,一旦没有竞争到锁,都会去进行排队,当锁被唤醒时,都是去唤醒最前面的锁,因此非公平锁只是体现在了线程加锁的过程,而没有体现在线程被唤醒的阶段。ReentrantLock是可重入锁,不管是公平锁还是非公平锁都是可重入的,也就是说在线程获取锁后,线程接下来实现的代码如果还需要用到这把锁,可以直接获取这把锁。

Sychronized的锁升级过程是怎样的?

  1. 偏向锁:锁对象的对象头中会记录当前获取到该锁的线程ID,如果线程下次又来获取该锁就可以直接获取,也就是支持锁重入。

  2. 轻量级锁:有偏向锁升级而来,当一个线程获取到锁后,这把锁就是偏向锁,此时如果有第二个线程来竞争锁,偏向锁就会升级为轻量级锁,它并没有去调用操作系统层面的api,而是通过自旋去实现,并不会阻塞线程。

  3. 重量级锁:当一个线程不断的通过自旋的方式想来获取一把锁时,当自旋次数过多仍然没有获得这把锁时,那么这把锁就会升级为重量级锁。尽量不要使锁变为重量级锁,因为重量级锁的阻塞和唤醒都是要消耗操作系统的资源的,系统的性能会更低。

  4. 自旋锁:线程会不断的循环,会不断的去判断当前锁的标记是不是为0(假设为0说明没有线程使用这把锁),如果不是为0,就会一直判断。

你可能感兴趣的:(Java知识点,java)