线程和进程的基础知识入门三部曲-2

75 阅读3分钟
本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
线程和进程的理论知识入门1参见:[传送门](https://juejin.cn/post/7209234917289164859)

volatile关键字

最轻量的同步机制。

  • volatile保证不同线程对共享变量操作的可见性,也就是说一个线程修改了volatile修饰的变量,当修改写回主内存时,另外一个线程立即看到最新的值。
  • 适合一写多读的场景。
  • volatile可以看做是轻量版的synchronized,volatile不保证原子性,但是如果是对一个共享变量进行多个线程的赋值,而没有其他的操作,那么就可以用volatile来代替synchronized,因为赋值本身是有原子性的,而volatile又保证了可见性,所以就可以保证线程安全了。

ThreadLocal的使用

ThreadLocal线程变量

  • 让每个 Thread 内有自己的实例副本,且该副本只能由当前 Thread 使用。这也是 ThreadLocal 命名的由来。
  • 既然每个 Thread 有自己的实例副本,且其它 Thread 不可访问,那就不存在多线程间共享的问题。
  • ThreadLocal 变量通常被private static修饰。当一个线程结束时,它所使用的所有 ThreadLocal 相对的实例副本都可被回收。

ThreadLocal的实现

  1. 每个Thread线程内部都有一个Map(ThreadLocalMap)
  2. Map里面存储ThreadLocal对象(key)和线程的变量副本(value)
  3. Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。
  4. 对于不同的线程,每次获取变量值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰。

ThreadLocal引发的内存泄漏分析

    
  static class ThreadLocalMap {

  static class Entry extends WeakReference<ThreadLocal<?>> {
      /** The value associated with this ThreadLocal. */
      Object value;

      Entry(ThreadLocal<?> k, Object v) {
          super(k);
          value = v;
      }
  }
  ...

ThreadLocal的实现原理,每一个Thread维护一个ThreadLocalMap,key为使用弱引用的ThreadLocal实例,value为线程变量的副本。

如果一个ThreadLocal不存在外部强引用时,key势必会被GC回收,这样就会导致ThreadLocalMap中的key为null,而value还存在着强引用,只有thread线程退出以后,value的强引用链条才会断掉。

但如果这些线程迟迟不结束的话,这些key为null的Entry的value就会一直存在着一条强引用链条,永远无法回收,造成内存泄漏。

ThreadLocal的正确使用

  • 每次使用完,调用其remove()方法清除掉。
  • 将ThreadLocal变量定义成private static,这样就会一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉。

线程间的协作-等待和通知

wait()和notify()是oject的方法。

wait()

syc(对象){
    while(条件不满足){
        对象.wait()
    }
    业务逻辑()
}

两个线程持有同一锁的时候,当其中一个线程进入wait()状态,会释放锁的,另外一个线程可以获取到锁。

notify()和notifyAll()

syc(对象){
    业务逻辑(),改变条件
    对象.notify()或者notifyAll()
}

yield()、sleep()、wait()、notify()方法对锁有什么影响?

  • yield()只是抢过了CPU的执行权,锁是不会释放的。
  • sleep()也不会释放当前线程所持有的锁。
  • wait()被调用后,会释放当前线程所持有的锁。
  • notify()被调用后,被锁无影响。