美文网首页
Java并发编程

Java并发编程

作者: 我要离开浪浪山 | 来源:发表于2023-06-23 21:35 被阅读0次

    1、volatile,最轻量的同步机制

    volatile 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某
    个变量的值,这新值对其他线程来说是立即可见的。

    参见代码:

    image.png
    不加 volatile 时,子线程无法感知主线程修改了 ready 的值,从而不会退出循环,
    而加了 volatile 后,子线程可以感知主线程修改了 ready 的值,迅速退出循环。
    但是 volatile 不能保证数据在多个线程下同时写时的线程安全,
    volatile 最适用的场景:一个线程写,多个线程读。

    2、ThreadLocal 和 Synchonized的比较

    ThreadLocal 和 Synchonized 都用于解决多线程并发訪问。可是 ThreadLocal
    与 synchronized 有本质的差别。synchronized 是利用锁的机制,使变量或代码块
    在某一时该仅仅能被一个线程訪问。而 ThreadLocal 为每个线程都提供了变量的
    副本,使得每个线程在某一时间訪问到的并非同一个对象,这样就隔离了多个线
    程对数据的数据共享。

    3、ThreadLocal 的使用

    ThreadLocal 类接口很简单,只有 4 个方法,我们先来了解一下:

    • void set(Object value)

    设置当前线程的线程局部变量的值。

    • public Object get()

    该方法返回当前线程所对应的线程局部变量。

    • public void remove()

    将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是 JDK
    5.0 新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动
    被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它
    可以加快内存回收的速度。

    • protected Object initialValue()

    返回该线程局部变量的初始值,该方法是一个 protected 的方法,显然是为
    了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第 1 次调用 get()
    或 set(Object)时才执行,并且仅执行 1 次。ThreadLocal 中的缺省实现直接返回一
    个 null。

    public final static ThreadLocal<String> RESOURCE = new
    ThreadLocal<String>();RESOURCE代表一个能够存放String类型的ThreadLocal对象。
    此时不论什么一个线程能够并发访问这个变量,对它进行写入、读取操作,都是
    线程安全的。

    4、引发的内存泄漏分析

    根据我们前面对 ThreadLocal 的分析,我们可以知道每个 Thread 维护一个
    ThreadLocalMap,这个映射表的 key 是 ThreadLocal 实例本身,value 是真正需
    要存储的 Object,也就是说 ThreadLocal 本身并不存储值,它只是作为一个 key
    来让线程从 ThreadLocalMap 获取 value。仔细观察 ThreadLocalMap,这个 map
    是使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。
    因此使用了 ThreadLocal 后,

    引用链如图所示:

    image.png

    图中的虚线表示弱引用。

    这样,当把 threadlocal 变量置为 null 以后,没有任何强引用指向 threadlocal
    实例,所以 threadlocal 将会被 gc 回收。这样一来,ThreadLocalMap 中就会出现
    key 为 null 的 Entry,就没有办法访问这些 key 为 null 的 Entry 的 value,如果当前
    线程再迟迟不结束的话,这些 key 为 null 的 Entry 的 value 就会一直存在一条强
    引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,而这块 value 永
    远不会被访问到了,所以存在着内存泄露。

    只有当前 thread 结束以后,current thread 就不会存在栈中,强引用断开,
    Current Thread、Map value 将全部被 GC 回收。最好的做法是不在需要使用
    ThreadLocal 变量后,都调用它的 remove()方法,清除数据

    5、等待/通知机制

    是指一个线程 A 调用了对象 O 的 wait()方法进入等待状态,而另一个线程 B
    调用了对象 O 的 notify()或者 notifyAll()方法,线程 A 收到通知后从对象 O 的 wait()
    方法返回,进而执行后续操作。上述两个线程通过对象 O 来完成交互,而对象
    上的 wait()和 notify/notifyAll()的关系就如同开关信号一样,用来完成等待方和通
    知方之间的交互工作。

    notify():
    通知一个在对象上等待的线程,使其从 wait 方法返回,而返回的前提是该线程
    获取到了对象的锁,没有获得锁的线程重新进入 WAITING 状态。

    notifyAll():
    通知所有等待在该对象上的线程

    wait()
    调用该方法的线程进入 WAITING 状态,只有等待另外线程的通知或被中断
    才会返回.需要注意,调用 wait()方法后,会释放对象的锁

    wait(long)
    超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n 毫秒,如果没有
    通知就超时返回

    wait (long,int)
    对于超时时间更细粒度的控制,可以达到纳秒

    6、等待和通知的标准范式

    image.png image.png

    在调用 wait()、notify()系列方法之前,线程必须要获得该对象的对象级 别锁,即只能在同步方法或同步块中调用 wait()方法、notify()系列方法,进入 wait()方法后,当前线程释放锁,在从 wait()返回前,线程与其他线程竞争重新获得锁,执行 notify()系列方法的线程退出调用了 notifyAll 的 synchronized代码块的时候后,他们就会去竞争。如果其中一个线程获得了该对象锁,它就会继续往下执行,在它退出 synchronized 代码块,释放锁后,其他的已经被唤醒的线程将会继续竞争获取该锁,一直进行下去,直到所有被唤醒的线程都执行完毕。

    7、notify 和 notifyAll 应该用谁

    尽可能用 notifyall(),谨慎使用 notify(),因为 notify()只会唤醒一个线程,我
    们无法确保被唤醒的这个线程一定就是我们需要唤醒的线程。

    8、调用 yield() 、sleep()、wait()、notify()等方法对锁有何影响?

    • yield() 、sleep()被调用后,都不会释放当前线程所持有的锁。
    • 调用 wait()方法后,会释放当前线程持有的锁,而且当前被唤醒后,会重新
      去竞争锁,锁竞争到后才会执行 wait 方法后面的代码。
    • 调用 notify()系列方法后,对锁无影响,线程只有在 syn 同步代码执行完后才
      会自然而然的释放锁,所以 notify()系列方法一般都是 syn 同步代码的最后一行。

    9、Fork-Join 原理

    image.png

    10、工作密取

    • 即当前线程的 Task 已经全被执行完毕,则自动取到其他线程的 Task 池中取
      出 Task 继续执行。
    • ForkJoinPool 中维护着多个线程(一般为 CPU 核数)在不断地执行 Task,每
      个线程除了执行自己职务内的 Task 之外,还会根据自己工作线程的闲置情况去
      获取其他繁忙的工作线程的 Task,如此一来就能能够减少线程阻塞或是闲置的时
      间,提高 CPU 利用率。

    11、CountDownLatch

    闭锁,CountDownLatch 这个类能够使一个线程等待其他线程完成各自的工
    作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动
    所有的框架服务之后再执行。

    CountDownLatch 是通过一个计数器来实现的,计数器的初始值为初始任务
    的数量。每当完成了一个任务后,计数器的值就会减 1
    (CountDownLatch.countDown()方法)。当计数器值到达 0 时,它表示所有的已
    经完成了任务,然后在闭锁上等待 CountDownLatch.await()方法的线程就可以恢
    复执行任务。

    image.png

    12、Callable、Future和FutureTask

    image.png

    13、Exchange的作用、应用场景和实战

    ee29dad4af4b566a5c3b35f94103ee6.png

    14、Semaphore的作用、应用场景和实战

    057bffb9977123820a62fd737d48a47.png

    相关文章

      网友评论

          本文标题:Java并发编程

          本文链接:https://www.haomeiwen.com/subject/vquvydtx.html