JavaSE基础入门_015_高级多线程

高级多线程

线程池概念

  • 问题:

    • 线程是宝贵的内存资源、单个线程约占 1MB 空间,过多分配易造成内存溢出。

    • 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。

 

  • 线程池:

    • 线程容器,可设定线程分配的数量上限。

    • 将预先创建的线程对象存入池中,并重用线程池中的线程对象。

    • 避免频繁的创建和销毁。

 

线程池原理

  • 将任务提交给线程池,由线程池分配线程、运行任务,并在当前任务结束后复用线程。

 

创建线程池

  • 常用的线程池接口和类(所在包:java.util.concurrent):

    • Executor:线程池的顶级接口

    • ExecutorService:继承了Executor接口,线程池接口,可通过 submit( Runnable task ) 提交任务代码。

    • Executors工厂类【工具类】:通过此类可以获得一个线程池。

    • 通过 newFixedThreadPool ( int nThreads ) 获取固定数量的线程池. 参数: 指定线程中线程的数量.

    • 通过 newCatchedThreadPool ( ) 获取动态数量的线程池, 如不够则创建新的, 没有上限

 

Callable接口

public interface Callable<V>{
public V call() throws Exception;
}
  • JDK 5 加入, 与 Runnable 接口类似, 实现之后代表一个线程任务。

  • Callable 具有泛型返回值、可以声明异常。

 

Future接口

  • Future:表示将要完成任务的结果

  • 需求:使用两个线程,并发计算 1~50、50~100的和,在进行汇总计算

  • 表示 ExecutorService.submit( ) 所返回的状态结果, 就是 call( ) 的返回值

  • 方法: V get() 以阻塞形式等待 Future 中的异步处理结果 ( call() 的返回值 )

 

线程的同步

  • 同步:

    • 形容一次方法的调用, 同步一旦开始, 调用者必须等待该方法返回, 才能继续.

    • 注: 单条执行路径

 

线程的异步

  • 异步:

    • 形容一次方法调用, 异步一旦开始, 像是一次消息传递, 调用者告知之后立刻返回. 二者竞争时间片, 并发执行.

    • 注: 多条执行路径

 

Lock接口

  • JDK5 加入, 与 synchronized 比较, 显示定义, 结构更灵活.

  • 提供更多实用性方法, 功能更强大、性能更优越。

  • 常用方法:

    • void lock() //获取锁,如锁被占用,则等待
      boolean tryLock() //尝试获取锁(成功返回true。失败返回false,不阻塞)
      void unlock() //释放锁
  • 利用其自身的实现类来实现接口调用、创建锁对象。

 

重入锁

  • ReentrantLock:Lock 接口的实现类,与 synchronized 一样具有互斥锁功能。

 

读写锁

  • ReentrantReadWriteLock:

    • 一种支持一写多读的同步锁,读写分离,可分别分配读锁、写锁。

    • 支持多次分配读锁,使多个读操作可以并发执行。

    • 读锁不互斥、写锁才互斥。

  • 互斥规则:

    • 写—写:互斥,阻塞

    • 读—写:互斥,读阻塞写、写阻塞读

    • 读—读:不互斥、不阻塞

    • 在读操作远远高于写操作的环境中,可在保障线程安全的情况下,提高运行效率。

     

 

线程安全的集合

  • Collection体系集合中, 除Vector以外的线程安全集合.

 

Collections中的工具方法

  • Collections工具类提供了多个可以获得线程安全集合的方法.

    • public static <T> Collection <T> synchronizedCollection(Collection<T> c)
      public static <T> List <T> synchronizedList(List<T> list)
      public static <T> Set <T> synchronizedSet(Set<T> s)
      public static <T> Map <K,V> synchronizedMap(Map<K,V> m)
      public static <T> SortedSet <T> synchronizedSortedSet(SortedSet<T> s)
      public static <T> SortedMap <K,V> synchronizedSortedMap(SortedMap<K,V> m)
  • JDK 1.2 提供, 接口统一、维护性高,但性能没有提升,均以 synchronized 实现。

 

CopyOnWriteArrayList

  • 线程安全的 ArrayList,加强版的读写分离。

  • 写有锁,读无锁,读写之间不阻塞,优于读写所。

  • 写入时,先 copy 一个容器副本、再添加新元素,最后替换引用。

  • 使用方式和 ArrayList 无异。

 

CopyOnWriteArraySet

  • 线程安全的Set,底层使用 CopyOnWriteArrayList 实现。

  • 唯一不同在于,使用 addIfAbsent() 添加元素,会遍历数组,

  • 如存在元素,则不添加(扔掉副本)。

  • 注意: 它是有序的

 

Queue接口(队列)

  • Collection的子接口, 表示队列 FIFO 先进先出.

  • 常用方法:

    • 抛出异常:

      • boolean add(E e) // 顺序添加一个元素 (到达上限后, 再添加则会抛出异常)
        E remove() // 获得第一个元素并移除 (如果队列没有元素时, 则抛出异常)
        E element() // 获得第一个元素但不移除 (如果队列没有元素时, 则抛出异常)
    • 返回特殊值: 推荐使用

      • boolean offer(E e) // 顺序添加一个元素 (到达上限后, 再添加则会返回false)
        E poll() // 获得第一个元素并移除 (如果队列没有元素时, 则返回null)
        E peek() // 获得第一个元素但不移除 (如果队列没有元素时, 则返回null)

 

ConcurrentLinkedQueue

  • 线程安全、可高效读写的队列,高并发下性能最好的队列。

  • 无锁、CAS比较交换算法,修改的方法包含三个核心参数(V,E,N)

  • V:要更新的变量、E:预期值、N:新值

  • 只有当 V==E时,V=N;否则表示已被更新过,则取消当前操作。

 

BlockingQueue接口 (阻塞队列)

  • Queue的子接口,阻塞的队列,增加了两个线程状态为无期限等待的方法。

  • 方法:

    • void put(E e) //将指定元素插入此队列中,如果没有可用空间,则等待。
      E take() //获取并移除此队列头部元素,如果没有可用元素,则等待
  • 可用于解决生产者、消费者问题

  • 当队列数量达到上限时,自动阻塞当前进程。

 

阻塞队列

  • ArrayBlockingQueue:

    • 数组结构实现,有界队列。(手工固定上限)

  • LinkedBlockingQueue:

    • 链表结构实现,有界队列。(默认上限 Integer. MAX_VALUE)

 

ConcurrentHashMap

  • 初始容量默认为16段(Segment),使用分段锁设计。

  • 不对整个Map加锁,而是为每个 Segment 加锁。

  • 当多个对象存入同一个Segment时,才需要互斥。

  • 最理想状态为16个对象分别存入16个Segment,并行数量16。

  • 使用方式与 HashMap 无异。

 

 

总结

  • ExecutorService 线程池接口、Executors 工厂。

  • Callable 线程任务、Future 异步返回值。

  • Lock、ReentrantLock 重入锁、ReentrantReadWriteLock 读写锁.

  • CopyOnWriteArrayList 线程安全的 ArrayList。

  • CopyOnWriteArraySet 线程安全的 Set。

  • ConcurrentLinkedQueue 线程安全的 Queue。

  • ArrayBlockingQueue 线程安全的阻塞 Queue。(生产者、消费者)

  • ConcurrentHashMap 线程安全的 HashMap。

原文地址:https://www.cnblogs.com/77-is-here/p/13079948.html