概述

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

本文重点介绍jdk中concurrent的内容,并发相关基础不在介绍,如果想系统的学习,建议直接看并发大作《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相关的知识点,仅供参考。
最后再附两篇博文,有兴趣的可以看下:

图学java基础篇之并发的更多相关文章

  1. 图学java基础篇之IO

    java io体系 如图可以看出,java的io按照包来划分的话可以分为三大块:io.nio.aio,但是从使用角度来看,这三块其实揉杂在一起的,下边我们先来概述下这三块: io:主要包含字符流和字节 ...

  2. 图学java基础篇之集合

    (本文部分图片引用自其他博客,最后有链接,侵删.由于笔记使用markdown记录,格式可能不是太好看,见谅) 集合结构 红字为java.util包下的,绿字为concurrent包下扩展的与并发相关的 ...

  3. 图学java基础篇之集合工具

    两个工具类 java.utils下又两个集合相关_(准确来说其中一个是数组的)_的工具类:Arrays和Collections,其中提供了很多针对集合的操作,其中涵盖了一下几个方面: 拷贝.填充.反转 ...

  4. 金三银四跳槽季,BAT美团滴滴java面试大纲(带答案版)之一:Java基础篇

    Java基础篇: 题记:本系列文章,会尽量模拟面试现场对话情景, 用口语而非书面语 ,采用问答形式来展现.另外每一个问题都附上“延伸”,这部分内容是帮助小伙伴们更深的理解一些底层细节的补充,在面试中可 ...

  5. 小白—职场之Java基础篇

    java基础篇 java基础 目录 1.java是一种什么语言,jdk,jre,jvm三者的区别 2.java 1.5之后的三大版本 3.java跨平台及其原理 4.java 语言的特点 5.什么是字 ...

  6. Java基础篇(JVM)——类加载机制

    这是Java基础篇(JVM)的第二篇文章,紧接着上一篇字节码详解,这篇我们来详解Java的类加载机制,也就是如何把字节码代表的类信息加载进入内存中. 我们知道,不管是根据类新建对象,还是直接使用类变量 ...

  7. java基础篇---I/O技术

    java基础篇---I/O技术   对于任何程序设计语言而言,输入输出(I/O)系统都是比较复杂的而且还是比较核心的.在java.io.包中提供了相关的API. java中流的概念划分 流的方向: 输 ...

  8. java基础篇---HTTP协议

    java基础篇---HTTP协议   HTTP协议一直是自己的薄弱点,也没抽太多时间去看这方面的内容,今天兴致来了就在网上搜了下关于http协议,发现有园友写了一篇非常好的博文,博文地址:(http: ...

  9. java基础篇---I/O技术(三)

    接上一篇java基础篇---I/O技术(二) Java对象的序列化和反序列化 什么叫对象的序列化和反序列化 要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象 ...

随机推荐

  1. 50个必备的jQuery代码段

    本文会给你们展示50个jquery代码片段,这些代码能够给你的javascript项目提供帮助.其中的一些代码段是从jQuery1.4.2才开始支持的做法,另一些则是真正有用的函数或方法,他们能够帮助 ...

  2. Aspx比较简单的登录

    客户端 <form id="form1" runat="server"> <div> 用户名:<input type=" ...

  3. Ubuntu下eclipse无法识别手机驱动(以小米2S为例子)

    google官方开发向导里对Android手机已经设置了允许安装非market程序,并且处于usb调试模式,但是仍然在usb连接电脑后无法被识别的问题作了解释. 在Ubuntu Linux环境下需要添 ...

  4. XShell远程连接本地虚机

    有很多朋友在自己电脑上部署完成了虚机,但是不知道怎么去用工具连接自己的虚机,下面给大家讲一下大概的步骤,不足之处敬请指正!! 1.打开我们的虚拟机平台,登录虚机 2.远程那肯定要知道虚机的IP地址,在 ...

  5. 从github克隆内容到本地时权限问题

    从github克隆内容到本地时权限问题

  6. Shape详解

    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...

  7. 报错:LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏

    参考原文:http://bbs.csdn.net/topics/390121452 项目>属性>配置属性>清单工具>输入和输出>嵌入清单:原来是“是”,改成“否” 如果上 ...

  8. SQL Server索引总结二

    从CREATE开始 通过显式的CREATE INDEX命令 在创建约束时作为隐含的对象 随约束创建的隐含索引 当向表中添加如下两种约束之一时,就会创建隐含索引. 主键约束(聚集索引) 唯一约束(唯一索 ...

  9. pat甲级1012

    1012 The Best Rank (25)(25 分) To evaluate the performance of our first year CS majored students, we ...

  10. java之打印机服务通俗做法

    javax.print包是API的主包,其中包含的类和接口能够让你:1)发现打印服务(Print Services)2)指定打印数据的格式 3)从一个打印服务创建打印工作(print jobs) 4) ...