图学java基础篇之并发

概述

并发处理自己就是编程开发重点之一,同时内容也很繁杂,从底层指令处理到上层应用开发都要涉及,也是最容易出问题的地方。这块知识也是评价一个开发人员水平的重要指标,本人自认为如今也只是学其皮毛,所以本文重点介绍java的并发相关体系,具体的点懂得就多讲,不懂得就给出参考文章。先来看图:

本文重点介绍jdk中concurrent的内容,并发相关基础不在介绍,若是想系统的学习,建议直接看并发大做《java并发编程实践》吧(以前看过,惋惜不少地方没懂),博客终究只是快餐,加强学习拓展知识很好,但打基础仍是要看书实战的。html

java内存模型

直接盗图一张(图寝删),详细讲解参考博客

java内存模型的做用能够理解为抽象了线程私有内存与主存(共享内存或堆)的关系,也就是原子性、可见性、顺序性的原则,然后介绍的内容都是为了保证这些原则的实现手段。java

实现多线程的方法

这一块其实没必要多说,你们都很熟悉,这里简单对比下优缺点:编程

  • 继承Thread:因为java不支持多继承,因此用起来有很强的局限性,使用场景很少。
  • 实现Runnable接口:经常使用的方式,有点对比Thread,而且更方便实现资源共享(二者异同,重点在评论)
  • 实现Callable接口: 结合Future使用,能够获取线程执行结果
  • Executor:concurrent中提供的一个上层并发处理框架,底层也是基于上边三种实现,基于此实现了线程池、任务调度等类,为一些典型场景提供了便捷的实现手段。

实现同步的方法

这一块也是经常使用的,也仅对比介绍一下:数组

  • volatile:volatile保证了原子操做在线程间的可见性(注意仅能保证可见性),而且修饰对象的操做不会指令重排。对于一个原子操做能够保证其之间一致,可是原子操做真的不多,好比i++都不行。更多介绍
  • Atomic:原子类,,基于CAS原理实现,提供了基本类型和引用对应的类,能保证其基本操做的可见性,底层基于volatile和Unsafe类(一个线程安全相关类,能够调用底层native方法)。若是线程间的同步仅限于某个值的改变,则可考虑用该类(实际上多数时候即便适合用也能找到对应的上层封装类,而没必要本身实现)
  • synchronized:最为经常使用的同步方法之一,能够分为同步方法和同步代码块两类,使用简单,惟一必定要搞清的是synchronized锁的对象是谁。(拓展:synchronized底层实现
  • wait/notify:同步几大原语之二(记得还有join吧),java的Object中已实现的native方法,经常使用来同步线程执行顺序或进度,然而多数场景concurrent也提供了对应工具类,因此通常使用时也应该优先使用对应的工具类
  • Lock:锁,主要有ReentrantLock和ReadWriteLock,该方式较synchronized的优点就是使用比较灵活,同步再也不局限于代码块或者方法,能够在任何须要的地方加锁解锁。就性能方面,除非你用的是1.4以前的jdk,不然二者差别不大
  • ThreadLocal:线程本地变量,其内部是一个map,key为线程对象自己,value为对应变量的一个拷贝,每一个线程使用该变量时实际使用的是其副本,以此解决多线程共享变量的竞争,须要注意的是改类型并非解决同步问题的,而是解决资源共享问题的,每一个线程使用各自的副本,相互之间不影响,可是该变量的值变化相互之间也是隔离的ThreadLocal深刻剖析

concurrent包

JDK 1.5增长了java.util.concurrent包,其内部提供了大量并发相关类,大大简化了设计并发的程序开发。该包大体可分为四大块:Atomic、Lock、Executor、以及线程安全的集合类,因为集合类已经在该系列第一篇介绍过,所以这里重点介绍前三块,此外还有一些工具类,这里仅介绍几个常见的。安全

Atomic

前边已经介绍过,Atomic为修饰的对象提供了原子更新,保证了其更新在线程间的可见性,Atomic包内的原子类实现主要基于CAS原理,利用了Unsafe包提供的CAS方法,其中能够分为如下几类:多线程

  • 原子更新基本类型类,提供了AtomicBoolean、AtomicInteger和AtomicLong,对于其余基本类型,能够参照其实现自行经过Unsafe的方法实现(实质上都是转成int处理)
  • 原子更新数组类:也提供了三种类型:AtomicIntegerArray、AtomicLongArray和AtomicReferenceArray
  • 原子更新引用类型:对于非基本类型提供的类,AtomicReference、AtomicReferenceFieldUpdater、AtomicMarkableReference
  • 原子更新字段类:这几个类主要用于更新类中的某个字段:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference
    这些类使用方法相似,都是经过如下方法实现原子更新:
  • getAndSet(v):设置新值,返回旧值
  • compareAndSet(expectedValue, newValue):若是当前值(current value)等于期待的值(expectedValue), 则原子地更新指定值为新值(newValue), 若是更新成功,返回true, 不然返回false, 换句话能够这样说: 将原子变量设置为新的值, 可是若是从我上次看到的这个变量以后到如今被其余线程修改了(和我指望看到的值不符), 那么更新失败
    详细的使用能够参考Java中的Atomic包使用指南

Lock

Lock提供了一种更为灵活的同步方式,Lock下主要有如下类:并发

  • ReentrantLock:重入锁,其底层经过一个Sync的静态类实现加锁解锁。Sync继承自AbstractQueuedSynchronizer,该抽象类内部实现了一个链表,保存了请求获取锁的状态,同时其内部有一个状态值,用来表示锁是否被线程获取,其修改经过Unsafe包提供的CAS方法,以此能够保证其可见性。同时尤为内部实现也能够知道,之因此叫重入锁,是由于其提供了非阻塞的使用方式,若是使用tryLock方法,当lock时在发现锁已lock的状况下,会当即返回结果,而不会阻塞。使用示例
  • ReentrantReadWriteLock:可重入读写锁。实现原理和ReentrantLock同样,再次基础上增长了读写锁的操做。使用示例
  • Condition:实现类是AbstractQueuedSynchronizer中的一个内部类,其功能是实现线程间的协调通讯,使得某个,或者某些线程一块儿等待某个条件(Condition),只有当该条件具有( signal 或者 signalAll方法被带调用)时 ,这些等待线程才会被唤醒,从而从新争夺锁。使用示例
  • LockSupport:能够看作对Unsafe包中并发原语的封装,为上层的锁实现提供原语操做。通常状况下上层开发不多用到。

Executor

Executor框架是concurrent中最经常使用的内容之一,其提供了一套可以便捷管理线程和任务的类,使咱们可以很方便建立、调度一批具备同类操做的线程。其主要有如下几个类(接口):框架

  • Callable、Future:前边已经介绍过了,提供了一种能够获取返回值的线程实现方法,通常与
    ExecutorService配合使用。Callable使用
  • Executor:线程工具类,主要用于线程池、ThreadFactory、Callable实例。Executors详解
  • ThreadFactory:接口类,提供了建立线程个工厂类,可能是时候配合线程池,做为参数传入为线程池提供建立线程的方法。
  • ExecutorService:接口类,是线程池实现类ThreadPoolExecutor的基类。
  • ExecutorCompletionService:与ExecutorService功能同样,不过其提供了poll()和take()两个方法用于获取线程执行结果,前者是非阻塞的,后者是阻塞的。
    使用示例

other

此外concurrent包还提供了一些其余类,这里仅列出功能,具体的使用可自行查询,这里给出一个比较完善的总结java.util.concurrent 用户指南工具

  • ForkJoinPool:和ExecutorService相似,不过其提供了fork和join的功能,可以将池内指定级别的任务进行分解或合并。这种处理方式与目前不少流处理框架相似,不过该实现使用的很少。
  • CountDownLatch:CountDownLatch 是一个线程协调器,它容许一个或多个线程等待一系列指定操做的完成。
    CountDownLatch 以一个给定的数量初始化。countDown() 每被调用一次,这一数量就减一。经过调用await() 方法之一,线程能够阻塞等待这一数量到达零。
  • CyclicBarrier:CyclicBarrier类也是一种同步机制,它能够在指定位置设定barrier,只有全部线程都执行到该位置是,才会继续向下执行。
  • Exchanger:该类提供了线程间交换数据的方法,能够视做一个管道。实现原理
  • Semaphore:信号量,学过操做系统的都知道,实现生产者—消费者的经常使用手段之一,信号量最大的用处是能够控制资源的访问数量,比起阻塞队列更加灵活。使用示例

其余

正如最开始所说的,并发相关的知识毫不是看一些知识点就能够掌握的,若是想要真正掌握,必须系统的学习。这里只是列出一些java相关的知识点,仅供参考。
最后再附两篇博文,有兴趣的能够看下:性能

相关文章
相关标签/搜索