原子性操作

解释:原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。即使在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰

一、原子性方法

java.util.concurrent.atomic包

  1. AtomicInteger类提供了方法incrementAndGetdecrementAndGet,它们分别以原子方式将一个整数自增或自减
  2. 实现跟踪不同线程是否在操作~compareAndSet方法会映射到一个处理器操作,比使用锁速度更快
  3. updateAndGet()accumulateAndGet()~利用二元操作符来合并操作原子值和所提供的参数
  4. getAndUpdate()getAndAccumulate()~可以返回原值

如果有大量线程要访问使用~LongAdderLongAccumulator

二、线程局部变量

简介:有时可能要避免共享变量,使用ThreadLocal类为各个线程提供各自的实例。

  1. staticThreadLocalwithInitial(Supplier<?extends S>supplier)创建一个线程局部变量,其初始值通过调用给定的supplier生成。
  2. protected initialize()~~应覆盖这个方法来提供一个初始值。默认情况下,这个方法返回null。
  3. T get()~~得到这个线程的当前值。如果是首次调用get,会调用initialize来得到这个值。
  4. void set(T t)~~为这个线程设置一个新值。
  5. void remove()~~删除对应这个线程的值。
  6. static ThreadLocalRandom current()~~返回特定于当前线程的Random类实例(生成随机数)

为什么弃用stop和suspend方法

stop方法,该方法直接终止该线程相关的所有未结束的方法

  • 在希望停止线程的时候应该中断线程,被中断的线程会在安全的时候停止。

suspend方法,挂起一个持有一个锁的线程,那么,该锁在恢复之前是不可用的,造成阻塞

  • 安全地挂起线程,可以引入一个变量suspendRequested并在run方法的某个安全的地方测试它,
  • 安全的地方是指该线程没有封锁其他线程需要的对象的地方。当该线程发现suspendRequested变量已经设置,将会保持等待状态直到它再次获得为止。

阻塞队列

简介:不需要显式的线程同步,这也是一种同步机制。
java.util.concurrent.BlockingQueue
image.png
在一个多线程程序中,队列会在任何时候空或满,因此,一定要使用offer、poll和peek方法作为替代,即插入null是非法的

java.util.concurrent

  1. LinkedBlockingQueue的容量是没有上边界的,但是,也可以选择指定最大容量

    • LinkedBlockingDeque是一个双端的
  2. ArrayBlockingQueue在构造时需要指定容量

    • 并且有一个可选的参数来指定是否需要公平性。则那么等待了最长时间的线程会优先得到处理
  3. PriorityBlockingQueue是一个带优先级的队列,用堆实现。

  4. DelayQueue包含实现Delayed接口的对象

    • getDelay方法返回对象的残留延迟(设置延时)。负值表示延迟已经结束。元素只有在延迟用完的情况下才能从DelayQueue移除。还必须实现compareTo方法。DelayQueue使用该方法对元素进行排序。
  5. TransferQueue接口,该线程等待另一个线程执行完~LinkedTransferQueue类实现了这个接口。


线程安全的集合

java.util.concurrent
ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet和ConcurrentLinkedQueue

  1. mappingCount()方法可以把大小作为long返回(类似size()方法)
  2. 集合返回弱一致性的迭代器。所以迭代器不一定能反映出它们被构造之后的所有的修改,但是,它们不会将同一个值返回两次,也不会抛出Concurrent ModificationException异常
  • 集合如果在迭代器构造之后发生改变,java.util包中的迭代器将抛出ConcurrentModificationException异常

    更新映射

    ConcurrentHashMap可以实现原子更新,ConcurrentHashMap中不允许有null值
  1. get和put方法不会破坏数据结构。不过,由于操作序列不是原子的,所以结果不可预知
  • 使用replace操作,它会以原子方式用一个新值替换原值,前提是之前没有其他线程把原值替换为其他值
  1. putIfAbsent()返回映射的的值(可能是原来的值,或者是新设置的值)
  2. compute()方法时可以提供一个键和一个计算新值的函数。
  • computeIfPresent()computeIfAbsent(),分别只在已经有原值的情况下计算新值,或者只有没有原值的情况下计算新值
  • merge方法,有一个参数表示键不存在时使用的初始值。否则,就会调用你提供的函数来结合原值与初始值,这个函数不处理键
  • 传入compute或merge的函数返回null,将从映射中删除现有的条目

散列映射的批量操作

  1. search为每个键或值提供一个函数,直到函数生成一个非null的结果。然后搜索终止,返回这个函数的结果
  2. reduce组合所有键或值,这里要使用所提供的一个累加函数
  3. forEach为所有键或值提供一个函数

上述操作的对应方法
#需要指定一个参数化阈值。如果映射包含的元素多于这个阈值,就会并行完成批操作
#将结果作为int、long和double输出,方法后缀加ToInt、ToLong和ToDouble即可

  • operationKeys:处理键。
  • operationValues:处理值。
  • operation:处理键和值。
  • operationEntries:处理Map.Entry对象。

并发集视图

  1. 静态newKeySet方法会生成一个Set
  2. keySet方法可以生成这个映射的键集。可以删除键并把值一并删除,不能执行添加操作
  • Java SE 8为ConcurrentHashMap增加了第二个keySet方法,形参传入一个默认值,在为集增加元素时使用

数组拷贝

  1. CopyOnWriteArrayList和CopyOnWriteArraySet

算法操作

  1. 静态Arrays.parallelSort方法可以对一个基本类型值或对象的数组排序
  2. parallelSetAll方法会用由一个函数计算得到的值填充一个数组
  3. parallelPrefix方法,它会用对应一个给定结合操作的前缀的累加结果替换各个数组元素

    Vector

    简介:Vector和Hashtable类就提供了线程安全的动态数组和散列表的实现。现在这些类被弃用了,
    取而代之的是ArrayList和HashMap类

  4. 任何集合类都可以通过使用同步包装器变成线程安全的

  • List> list = Collections.singletonList(new ArrayList());
  1. 如果在另一个线程可能进行修改时要对集合进行迭代,仍然需要使用对象方法锁

异步操作

Callable与Future

简介:Runnable封装一个异步运行任务,Callable与Runnable类似,但是有返回值。Callable接口是一个参数化的类型,只有一个方法call

Future

简介:Future保存异步计算的结果。进行一个计算,将Future对象交给某个线程,Future对象的所有者在结果计算好之后就可以获得它

  1. get()~调用后被阻塞,直到计算完成。可以设置超时,超时会报异常,如果该方法的线程被中断会报异常
  2. isDone()~检测是否在计算是否结束
  3. cance()~法取消该计算

FutureTask包装器

简介:可将Callable转换成Future和Runnable,它同时实现二者的接口


线程池

image.png

  1. newCachedThreadPool()~对于每个任务,如果有空闲线程可用,立即让它执行任务,如果没有可用的空闲线程,则创建一个新线程。
  2. newFixedThreadPool()~如果提交的任务数多于空闲的线程数,那么把得不到服务的任务放置到队列中。当其他任务完成以后再运行它们。
  3. newSingleThreadExecutor()由一个线程去执行提交的任务,一个接着一个执行。

一、创建线程池

  • ExecutorService service = Executors._newCachedThreadPool_();

二、获取线程

  • service.submit(new Thread());
  • 调用submit时,会得到一个Future对象,可用来查询该任务的状态,或者可以使用这个对象来调用isDone、cancel或isCancelled。但是,get方法在完成的时候只是简单地返回null

三、关闭线程池

  • shutdown,被关闭的执行器不再接受新的任务。当所有任务都完成以后,线程池中的线程死亡。
  • shutdownNow,该池取消尚未开始的所有任务并试图中断正在运行的线程
  • 注意:如果提交Callable对象,那就要保存好返回的Future对象

预执行

简介:ScheduledExecutorService接口具有为预定执行或重复执行任务而设计的方法
使用:设置延时执行的时间或重复执行的周期
创建:Executors类的newScheduledThreadPoolnewSingleThreadScheduledExecutor方法

  • 返回实现了Scheduled-ExecutorService接口的对象

控制任务组

简介:控制一组相关任务

  1. invokeAny方法提交所有对象到一个Callable对象的集合中,并返回某个已经完成了的任务的结果。
  • 这个方法的缺点是如果第一个任务恰巧花去了很多时间,则可能不得不进行等待全部完成。

image.png

  1. 将结果按可获得的顺序保存。可以用ExecutorCompletionService来对结果进行排列。

image.png


Fork-Join框架