关键词:
JMM
、volatile
、synchronized
、final
、Happens-Before
、内存屏障
摘要:Java 内存模型(Java Memory Model),简称 JMM。Java 内存模型的目标是为了解决由可见性和有序性导致的并发安全问题。Java 内存模型通过 屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。
synchronized 的简介
synchronized
是 Java 中的关键字,是 利用锁的机制来实现互斥同步的。
synchronized
可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块。
如果不需要 Lock
、ReadWriteLock
所提供的高级同步特性,应该优先考虑使用 synchronized
,理由如下:
对于简单的并行任务,你可以通过“线程池 +Future”的方案来解决;如果任务之间有聚合关系,无论是 AND 聚合还是 OR 聚合,都可以通过 CompletableFuture 来解决;而批量的并行任务,则可以通过 CompletionService 来解决。
CompletableFuture
runAsync 和 supplyAsync 方法
CompletableFuture 提供了四个静态方法来创建一个异步操作。
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
同步容器
同步容器简介
在 Java 中,同步容器主要包括 2 类:
Vector
、Stack
、Hashtable
Vector
-Vector
实现了List
接口。Vector
实际上就是一个数组,和ArrayList
类似。但是Vector
中的方法都是synchronized
方法,即进行了同步措施。Stack
-Stack
也是一个同步容器,它的方法也用synchronized
进行了同步,它实际上是继承于Vector
类。Hashtable
-Hashtable
实现了Map
接口,它和HashMap
很相似,但是Hashtable
进行了同步处理,而HashMap
没有。
Collections
类中提供的静态工厂方法创建的类(由Collections.synchronizedXXX
等方法)
本文先阐述 Java 中各种锁的概念。
然后,介绍锁的核心实现 AQS。
然后,重点介绍 Lock 和 Condition 两个接口及其实现。并发编程有两个核心问题:同步和互斥。
互斥,即同一时刻只允许一个线程访问共享资源;
同步,即线程之间如何通信、协作。
这两大问题,管程(
sychronized
)都是能够解决的。J.U.C 包还提供了 Lock 和 Condition 两个接口来实现管程,其中 Lock 用于解决互斥问题,Condition 用于解决同步问题。
原子变量类简介
为何需要原子变量类
保证线程安全是 Java 并发编程必须要解决的重要问题。Java 从原子性、可见性、有序性这三大特性入手,确保多线程的数据一致性。
- 确保线程安全最常见的做法是利用锁机制(
Lock
、sychronized
)来对共享数据做互斥同步,这样在同一个时刻,只有一个线程可以执行某个方法或者某个代码块,那么操作必然是原子性的,线程安全的。互斥同步最主要的问题是线程阻塞和唤醒所带来的性能问题。 volatile
是轻量级的锁(自然比普通锁性能要好),它保证了共享变量在多线程中的可见性,但无法保证原子性。所以,它只能在一些特定场景下使用。- 为了兼顾原子性以及锁带来的性能问题,Java 引入了 CAS (主要体现在
Unsafe
类)来实现非阻塞同步(也叫乐观锁)。并基于 CAS ,提供了一套原子工具类。
Java 对于并发的支持主要汇聚在
java.util.concurrent
,即 J.U.C。而 J.U.C 的核心是AQS
。
J.U.C 简介
Java 的 java.util.concurrent
包(简称 J.U.C)中提供了大量并发工具类,是 Java 并发能力的主要体现(注意,不是全部,有部分并发能力的支持在其他包中)。从功能上,大致可以分为:
- 原子类 - 如:
AtomicInteger
、AtomicIntegerArray
、AtomicReference
、AtomicStampedReference
等。 - 锁 - 如:
ReentrantLock
、ReentrantReadWriteLock
等。 - 并发容器 - 如:
ConcurrentHashMap
、CopyOnWriteArrayList
、CopyOnWriteArraySet
等。 - 阻塞队列 - 如:
ArrayBlockingQueue
、LinkedBlockingQueue
等。 - 非阻塞队列 - 如:
ConcurrentLinkedQueue
、LinkedTransferQueue
等。 Executor
框架(线程池)- 如:ThreadPoolExecutor
、Executors
等。
关键词:
Thread
、Runnable
、Callable
、Future
、wait
、notify
、notifyAll
、join
、sleep
、yeild
、线程状态
、线程通信
线程简介
简介
什么是线程池
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。
为什么要用线程池
如果并发请求数量很多,但每个线程执行的时间很短,就会出现频繁的创建和销毁线程。如此一来,会大大降低系统的效率,可能频繁创建和销毁线程的时间、资源开销要大于实际工作的所需。
正是由于这个问题,所以有必要引入线程池。使用 线程池的好处 有以下几点:
- 降低资源消耗 - 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度 - 当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性 - 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。但是要做到合理的利用线程池,必须对其原理了如指掌。
JDK 的
java.util.concurrent
包(即 J.U.C)中提供了几个非常有用的并发工具类。
CountDownLatch
字面意思为 递减计数锁。用于控制一个线程等待多个线程。
CountDownLatch
维护一个计数器 count,表示需要等待的事件数量。countDown
方法递减计数器,表示有一个事件已经发生。调用await
方法的线程会一直阻塞直到计数器为零,或者等待中的线程中断,或者等待超时。