5 Volatile
Java 允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。
5.1 解决内存可见性问题
private volatile boolean flag = true;
PS: 内存屏障( Memory Barrier )是一种 CPU 指令,用于控制特定条件下的重排序和内存可见性问题。 Java 编译器也会根据内存屏障的规则禁止重排序
- StoreStore屏障可以保证在volatile写之前,其前面的所有普通写操作都已经刷新到主内存中。
- StoreLoad屏障的作用是避免volatile写与后面可能有的volatile读/写操作重排序。
- LoadLoad屏障用来禁止处理器把上面的volatile读与下面的普通读重排序。
- LoadStore屏障用来禁止处理器把上面的volatile读与下面的普通写重排序。
5.2 原子性的问题
public class Demo3Volatile {
public static void main(String[] args) throws
InterruptedException {
VolatileDemo demo = new VolatileDemo();
Thread t = new Thread(demo);
t.start();
}
Thread.sleep(1000);
System.out.println(demo.count);
}
static class VolatileDemo implements Runnable {
public volatile int count;
//public volatile AtomicInteger count = new
AtomicInteger(0);
public void run() {
addCount();
}
public void addCount() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
}
}
1. 线程 1 读取 count 的值为 52. 线程 2 读取 count 的值为 53. 线程 2 加 1 操作4. 线程 2 最新 count 的值为 65. 线程 2 写入值到主内存的最新值为 6
public synchronized void addCount() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
//可重入锁
private Lock lock = new ReentrantLock();
public void addCount() {
for (int i = 0; i < 10000; i++) {
lock.lock();
count++;
lock.unlock();
}
}
public static AtomicInteger count = new AtomicInteger(0);
public void addCount() {
for (int i = 0; i < 10000; i++) {
//count++;
count.incrementAndGet();
}
}
5.3 Volatile 适合使用场景
5.4 synchronized和volatile比较
6 J.U.C之CAS
6.1 CAS介绍
![](https://img-blog.csdnimg.cn/direct/bc13fa88dc194cb9aa12511b46b3f648.png)
6.2 CAS原理剖析
再次测试之前Volatile的例子,把循环的次数调整为一亿(保证在一秒之内不能遍 历完成,从而测试三种原子操作的性能),我们发现, AtomicInteger原子操作性能 最高,他是用的就是CAS。
6.2.2 synchronized同步分析
注意,本小节是解释synchronized性能低效的原因,只要能理解synchronized同 步过程其实还需要做很多事,这些逻辑的执行都需要占用资源,从而导致性能较
低,是为了对比CAS的高效。这部分分析过于深入JMM底层原理,不适合初级甚至 中级程序员学习。
我们之前讲过, synchronized的同步操作主要是monitorenter和monitorexit这 两个jvm指令实现的,我们先写一段简单的代码:
public class Demo2Synchronized {
public void test2() {
synchronized (this) {
}
}
}
javac Demo2Synchronized.java
javap -c Demo2Synchronized.class
从结果可以看出,同步代码块是使用monitorenter和monitorexit这两个jvm指令实 现的:
monitorenter和monitorexit这两个jvm指令实现锁的使用,主要是基于 Mark Word和、 monitor。
Mark Word
Hotspot虚拟机的对象头主要包括两部分数据: Mark Word(标记字段)、 Klass Pointer (类型指针)。其中Klass Point是是对象指向它的类元数据的指针,虚拟机 通过这个指针来确定这个对象是哪个类的实例, Mark Word用于存储对象自身的运 行时数据,它是synchronized实现轻量级锁和偏向锁的关键。
年龄、锁状态标志、线程持有的锁、偏向线程 ID、偏向时间戳等等。Java对象头一 般占有两个机器码(在32位虚拟机中, 1个机器码等于4字节,也就是32bit),但是 如果对象是数组类型,则需要三个机器码,因为JVM虚拟机可以通过Java对象的元数 据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用 一块来记录数组长度。下图是Java对象头的存储结构(32位虚拟机)
对象头信息是与对象自身定义的数据无关的额外存储成本,但是考虑到虚拟机的 空间效率, Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储 尽量多的数据,它会根据对象的状态复用自己的存储空间,也就是说, Mark Word 会随着程序的运行发生变化,变化状态如下(32位虚拟机)
monitor
什么是Monitor?我们可以把它理解为一个同步工具,也可以描述为一种同步机 制,它通常被描述为一个对象。与一切皆对象一样,所有的Java对象是天生的 Monitor,每一个Java对象都有成为Monitor的潜质,因为在Java的设计中 ,每 一个Java对象都带了一把看不见的锁,它叫做内部锁或者Monitor锁。
Monitor 是线程私有的数据结构,每一个线程都有一个可用monitor record 列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor 关联(对象头的MarkWord中的LockWord指向monitor的起始地址),同时
monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个 线程占用
Owner:初始时为NULL表示当前没有任何线程拥有该monitor record,当线程 成功拥有该锁后保存线程唯一标识,当锁被释放时又设置为NULL;
· EntryQ:关联一个系统互斥锁(semaphore),阻塞所有试图锁住monitor record失败的线程。
· RcThis:表示blocked或waiting在该monitor record上的所有线程的个数。
Nest:用来实现重入锁的计数。
HashCode:保存从对象头拷贝过来的HashCode值(可能还包含GC age)。
Candidate:用来避免不必要的阻塞或等待线程唤醒,因为每一次只有一个线程 能够成功拥有锁,如果每次前一个释放锁的线程唤醒所有正在阻塞或等待的线 程,会引起不必要的上下文切换(从阻塞到就绪然后因为竞争锁失败又被阻
塞)从而导致性能严重下降。 Candidate只有两种可能的值0表示没有需要唤醒 的线程1表示要唤醒一个继任线程来竞争锁。
6.2.3 CAS原理
在上一部分,我们介绍了synchronized底层做了大量的工作,才实现同步,而同 步保证了原子操作。但是不可避免的是性能较低。 CAS是如何提高性能的呢?
CAS的思想很简单:三个参数, 一个当前内存值V、旧的预期值A、即将更新的值 B,当且仅当旧的预期值A和内存值V相同时,将内存值修改为B并返回true,否则什 么都不做,并返回false。如果CAS操作失败,通过自旋的方式等待并再次尝试,直 到成功。
CAS在 先比较后修改 这个CAS过程中,根本没有获取锁,释放锁的操作,是硬件 层面的原子操作,跟JMM内存模型没有关系。大家可以理解为直接使用其他的语
言,在JVM虚拟机之外直接操作计算机硬件,正因为如此,对比synchronized的同 步,少了很多的逻辑步骤,使得性能大为提高。
JUC下的atomic类都是通过CAS来实现的,下面就是一个AtomicInteger原子操作 类的例子,在其中使用了Unsafe unsafe = Unsafe.getUnsafe()。 Unsafe 是CAS的 核心类,它提供了硬件级别的原子操作
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//操作的值也进行了volatile修饰,保证内存可见性
private volatile int value;
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 +
var4));
return var5;
}
public final native boolean compareAndSwapInt(Object var1, long
var2, int var4, int var5);
6.3 native关键词
native 方法有效地扩充了 jvm ,实际上我们所用的很多代码已经涉及到这种方法了,通过非常简洁的接口帮我们实现 Java 以外的工作。
native优势:
1. 很多层次上用Java去实现是很麻烦的,而且Java解释执行的效率也差了c语言啥 的很多,纯Java实现可能会导致效率不达标,或者可读性奇差。
2. Java毕竟不是一个完整的系统,它经常需要一些底层的支持,通过JNI和native method我们就可以实现jre与底层的交互,得到强大的底层操作系统的支持,使 用一些Java本身没有封装的操作系统的特性。
6.4 多CPU的CAS处理
CAS可以保证一次的读-改-写操作是原子操作,在单处理器上该操作容易实现,但 是在多处理器上实现就有点儿复杂了。 CPU提供了两种方法来实现多处理器的原子 操作:总线加锁或者缓存加锁。
总线加锁:总线加锁就是就是使用处理器提供的一个LOCK#信号,当一个处理 器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独 占使用共享内存。但是这种处理方式显得有点儿霸道,不厚道,他把CPU和内 存之间的通信锁住了,在锁定期间,其他处理器都不能其他内存地址的数据,
其开销有点儿大。
缓存加锁:其实针对于上面那种情况我们只需要保证在同一时刻对某个内存地 址的操作是原子性的即可。缓存加锁就是缓存在内存区域的数据如果在加锁期 间,当它执行锁操作写回内存时,处理器不在输出LOCK#信号,而是修改内部 的内存地址,利用缓存一致性协议来保证原子性。缓存一致性机制可以保证同 一个内存区域的数据仅能被一个处理器修改,也就是说当CPU1修改缓存行中的i 时使用缓存锁定,那么CPU2就不能同时缓存了i的缓存行。
CAS缺陷
CAS虽然高效地解决了原子操作,但是还是存在一些缺陷的,主要表现在三个方 法:循环时间太长、只能保证一个共享变量原子操作、 ABA问题。
循环时间太长
如果CAS一直不成功呢?这种情况绝对有可能发生,如果自旋CAS长时间地不成 功,则会给CPU带来非常大的开销。在JUC中有些地方就限制了CAS自旋的次
数,例如BlockingQueue的SynchronousQueue。
只能保证一个共享变量原子操作
看了CAS的实现就知道这只能针对一个共享变量,如果是多个共享变量就只能使 用锁了。
ABA问题
CAS需要检查操作值有没有发生改变,如果没有发生改变则更新。但是存在这样 一种情况:如果一个值原来是A,变成了B,然后又变成了A,那么在CAS检查的 时候会发现没有改变,但是实质上它已经发生了改变,这就是所谓的ABA问题。 对于ABA问题其解决方案是加上版本号,即在每个变量都加上一个版本号,每次 改变时加1,即A —> B —> A,变成1A —> 2B —> 3A。
CAS的ABA隐患问题,Java提供了AtomicStampedReference来解决。
AtomicStampedReference通过包装[E,Integer]的元组来对对象标记版本戳 stamp,从而避免ABA问题。对于上面的案例应该线程1会失败。
下面我们将通过一个例子可以可以看到AtomicStampedReference和
AtomicInteger的区别。我们定义两个线程,线程1负责将100 —> 110 —> 100,线 程2执行 100 —>120,看两者之间的区别。
public class Demo4ABA {
private static AtomicInteger ai = new AtomicInteger(100);
private static AtomicStampedReference air = new
AtomicStampedReference(100, 1);
//ABA问题演示:
//1. 线程1先对数据进行修改 A-B-A过程
//2. 线程2也对数据进行修改 A-C的过程
public static void main(String[] args) throws
InterruptedException {
// AtomicInteger可以看到不会有任何限制随便改
// 线程2修改的时候也不可能知道要A-C 的时候,A是原来的A还是修改之后 的A
Thread at1 = new Thread(new Runnable() {
public void run() {
ai.compareAndSet(100, 110);
ai.compareAndSet(110, 100);
}
});
Thread at2 = new Thread(new Runnable() {
public void run() {
try {
//为了让线程1先执行完,等一会
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("AtomicInteger:" + ai.compareAndSet(100, 120));
System.out.println("执行结果:" + ai.get());
}
});
//tsf2先获取stamp,导致预期时间戳不一致
int stamp = air.getStamp();
try {
TimeUnit.MILLISECONDS.sleep(100);
//线程tsf1执行完
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("AtomicStampedReference:" + air.compareAndSet(100, 120, stamp, stamp + 1));
int[] stampArr = {stamp + 1};
System.out.println("执行结果:" +
air.get(stampArr));
}
运行结果充分展示了AtomicInteger的ABA问题和AtomicStampedReference解决 ABA问题。
7 J.U.C之atomic包
7.1 atomic包介绍
7.2 基本类型
getAndAdd(int) //增加指定的数据,返回变化前的数据
getAndDecrement() //减少1,返回减少前的数据
getAndIncrement() //增加1,返回增加前的数据
getAndSet(int) //设置指定的数据,返回设置前的数据
addAndGet(int) //增加指定的数据后返回增加后的数据
decrementAndGet() //减少1,返回减少后的值
incrementAndGet() //增加1,返回增加后的值
lazySet(int) //仅仅当get时才会set
compareAndSet(int, int)//尝试新增后对比,若增加成功则返回true否则返回false
compareAndSet(boolean, boolean) //参数1为原始值,参数2为修改的新值,若
修改成功返回true,否则返回false
getAndSet(boolean)// 尝试设置新的boolean值,直到成功为止,返回设置前的数据
7.4 引用类型
public class Demo5AtomicReference {
public static void main(String[] args) throws
InterruptedException {
User u1 = new User("张三", 22);
User u2 = new User("李四", 33);
AtomicReference ar = new AtomicReference(u1);
ar.compareAndSet(u1, u2);
}
static class User {
private String name;
public volatile int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
public class Demo6AtomicMrkableReference {
public static void main(String[] args) throws
InterruptedException {
User u1 = new User("张三", 22);
User u2 = new User("李四", 33);
//和AtomicStampedReference效果一样,用于解决ABA的
//区别是表示不是用的版本号,而只有true和false两种状态。相当于未修
改和已修改
AtomicMarkableReference<User> amr = new
AtomicMarkableReference(u1, true);
amr.compareAndSet(u1, u2, false, true);
System.out.println(amr.getReference());
}
static class User {
private String name;
public volatile int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
7.3 数组类型
addAndGet(int, int)//执行加法,第一个参数为数组的下标,第二个参数为增加的
数量,返回增加后的结果
compareAndSet(int, int, int)// 对比修改,参1数组下标,参2原始值,参3修
改目标值,成功返回true否则false
decrementAndGet(int)// 参数为数组下标,将数组对应数字减少1,返回减少后的
数据
incrementAndGet(int)// 参数为数组下标,将数组对应数字增加1,返回增加后的
数据
getAndAdd(int, int)// 和addAndGet类似,区别是返回值是变化前的数据
getAndDecrement(int)// 和decrementAndGet类似,区别是返回变化前的数据
getAndIncrement(int)// 和incrementAndGet类似,区别是返回变化前的数据
getAndSet(int, int)// 将对应下标的数字设置为指定值,第二个参数为设置的值,
返回是变化前的数据
public class Demo7AtomicIntegerArray {
public static void main(String[] args) throws
InterruptedException {
int[] arr = {1, 2, 3, 4, 5};
AtomicIntegerArray aia = new AtomicIntegerArray(arr);
aia.compareAndSet(1, 2, 200);
System.out.println(aia.toString());
}
}
//参数1:数组下标;
//参数2:修改原始值对比;
//参数3:修改目标值
//修改成功返回true,否则返回false
compareAndSet(int, Object, Object)
//参数1:数组下标
//参数2:修改的目标
//修改成功为止,返回修改前的数据
getAndSet(int, Object)
public class Demo8AtomicReferenceArray {
public static void main(String[] args) throws
InterruptedException {
User u1 = new User("张三", 22);
User u2 = new User("李四", 33);
User[] arr = {u1, u2};
AtomicReferenceArray<User> ara = new
AtomicReferenceArray<User>(arr);
System.out.println(ara.toString());
ara.compareAndSet(0, u1, u3);
System.out.println(ara.toString());
}
static class User {
private String name;
public volatile int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
7.5 对象的属性修改类型
AtomicIntegerFieldUpdater: 原子更新整形字段的更新器AtomicLongFieldUpdater :原子更新长整形字段的更新器AtomicReferenceFieldUpdater :原子更新引用类形字段的更新器
public class AtomicIntegerFieldUpdaterTest {
public static void main(String[] args) {
AtomicIntegerFieldUpdater<User> a =
AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
User user = new User("Java", 22);
System.out.println(a.get(user));
System.out.println(a.getAndAdd(user,10));
System.out.println(a.get(user));
}
}
class User {
private String name;
public volatile int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public final long incrementAndGet() {
for (;;) {
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
![](https://img-blog.csdnimg.cn/direct/0512710115904a269eccdf3421915e74.png)
一段LongAdder和Atomic的对比测试代码:
public class Demo9Compare {
public static void main(String[] args) {
AtomicLong atomicLong = new AtomicLong(0L);
LongAdder longAdder = new LongAdder();
for (int i = 0; i < 50; i++) {
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 1000000; j++) {
//atomicLong.incrementAndGet();
longAdder.increment();
}
}
}).start();
}
while (Thread.activeCount() > 2) {
}
System.out.println(atomicLong.get());
System.out.println(longAdder.longValue());
System.out.println("耗时:" + (System.currentTimeMillis()
- start));
}
}
8 J.U.C之AQS
8.1 AQS简介
8.1.1 AQS的作用
8.1.2 state状态
8.1.3 资源共享方式
8.2 CLH同步队列
![](https://img-blog.csdnimg.cn/direct/dbb93be014674e08af4f2dcdb9efaa85.png)
当前线程如果获取同步状态失败时, AQS则会将当前线程已经等待状态等信息构 造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步 状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态。
8.2.3 入列
CLH队列入列非常简单,就是tail指向新节点、新节点的prev指向当前最后的节 点,当前最后一个节点的next指向当前节点。
代码我们可以看看addWaiter(Node node)方法:
private Node addWaiter(Node mode) {
//新建Node
Node node = new Node(Thread.currentThread(), mode);
//快速尝试添加尾节点
Node pred = tail;
if (pred != null) {
node.prev = pred;
//CAS设置尾节点
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//多次尝试
enq(node);
return node;
}
8.2.4 出列
9 J.U.C之锁
9.1 锁的基本概念
9.1.1 互斥锁
9.1.2 阻塞锁
9.1.3 自旋锁
9.1.4 读写锁
9.1.5 公平锁
9.2 ReentrantLock
public ReentrantLock() {
//非公平锁
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
//公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
9.2.1 获取锁
//非公平锁
ReentrantLock lock = new ReentrantLock();
lock.lock();
public void lock() {
sync.lock();
}
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
9.2.2 释放锁
public void unlock() {
sync.release(1);
}
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
//减掉releases
int c = getState() - releases;
//如果释放的不是持有锁的线程,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//state == 0 表示已经释放完全了,其他线程可以获取同步状态了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
9.2.3 公平锁与非公平锁原理
public final boolean hasQueuedPredecessors() {
Node t = tail; //尾节点
Node h = head; //头节点
Node s;
//头节点 != 尾节点
//同步队列第一个节点不为null
//当前线程是同步队列第一个节点
return h != t &&
((s = h.next) == null || s.thread !=
Thread.currentThread());
}
9.2.4 ReentrantLock与synchronized的区别
9.3 读写锁ReentrantReadWriteLock
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
/** 内部类 读锁 */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** 内部类 写锁 */
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
/** 使用默认(非公平)的排序属性创建一个新的 ReentrantReadWriteLock */
public ReentrantReadWriteLock() {
this(false);
}
/** 使用给定的公平策略创建一个新的 ReentrantReadWriteLock */
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
writerLock = new WriteLock(this);
}
/** 返回用于写入操作的锁 */
public ReentrantReadWriteLock.WriteLock writeLock() { return
writerLock; }
/** 返回用于读取操作的锁 */
public ReentrantReadWriteLock.ReadLock readLock() { return
readerLock; }
abstract static class Sync extends AbstractQueuedSynchronizer {
//省略其余源代码
}
public static class WriteLock implements Lock,
java.io.Serializable{
//省略其余源代码
}
public static class ReadLock implements Lock,
java.io.Serializable {
//省略其余源代码
}
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
9.3.1 写锁的获取
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
//当前锁个数
int c = getState();
//写锁
int w = exclusiveCount(c);
if (c != 0) {
//c != 0 && w == 0 表示存在读锁
//当前线程不是已经获取写锁的线程
if (w == 0 || current != getExclusiveOwnerThread())
return false;
//超出最大范围
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
setState(c + acquires);
return true;
}
//是否需要阻塞
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
//设置获取锁的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
9.3.2 写锁的释放
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
//释放的线程不为锁的持有者
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
//若写锁的新线程数为0,则将锁的持有者设置为null
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
9.3.3 读锁的获取
public void lock() {
sync.acquireShared(1);
}
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
与写锁相同,读锁也提供了unlock()释放读锁
public void unlock() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
9.3.6 读写锁例子
public class Demo10ReentrantReadWriteLock {
private static volatile int count = 0;
public static void main(String[] args) {
ReentrantReadWriteLock lock = new
ReentrantReadWriteLock();
WriteDemo writeDemo = new WriteDemo(lock);
ReadDemo readDemo = new ReadDemo(lock);
for (int i = 0; i < 3; i++) {
new Thread(writeDemo).start();
}
for (int i = 0; i < 5; i++) {
new Thread(readDemo).start();
}
}
static class WriteDemo implements Runnable {
ReentrantReadWriteLock lock;
public WriteDemo(ReentrantReadWriteLock lock) {
this.lock = lock;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.writeLock().lock();
count++;
System.out.println("写锁:"+count);
}
}
}
static class ReadDemo implements Runnable {
ReentrantReadWriteLock lock;
public ReadDemo(ReentrantReadWriteLock lock) {
this.lock = lock;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.readLock().lock();
System.out.println("读锁:"+count);
lock.readLock().unlock();
}
}
}
}
10 J.U.C之Condition
10.1 Condition介绍
Condition提供了一系列的方法来对阻塞和唤醒线程:
1. await() :造成当前线程在接到信号或被中断之前一直处于等待状态。
2. await(longtime, TimeUnit unit) :造成当前线程在接到信号、被中断或到达 指定等待时间之前一直处于等待状态。
3. awaitNanos(long nanosTimeout) :造成当前线程在接到信号、被中断或到 达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在
nanosTimesout之前唤醒,那么返回值 = nanosTimeout – 消耗时间,如果返 回值 <= 0 ,则可以认定它已经超时了。
4. awaitUninterruptibly() :造成当前线程在接到信号之前一直处于等待状态。 【注意:该方法对中断不敏感】。
5. awaitUntil(Date deadline) :造成当前线程在接到信号、被中断或到达指定 最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true, 否则表示到了指定时间,返回返回false。
6. signal():唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition 相关的锁。
7. signal()All:唤醒所有等待线程。能够从等待方法返回的线程必须获得与 Condition相关的锁。
Condition是一种广义上的条件队列(等待队列)。他为线程提供了一种更为灵 活的等待/通知模式,线程在调用await方法后执行挂起操作,直到线程等待的某 个条件为真时才会被唤醒。 Condition必须要配合锁一起使用,因为对共享状态 变量的访问发生在多线程环境下。 一个Condition的实例必须与一个Lock绑定,
因此Condition一般都是作为Lock的内部实现。
10.2 Condition的实现
public class ConditionObject implements Condition,
java.io.Serializable {
}
10.2.1 等待队列
public class ConditionObject implements Condition,
java.io.Serializable {
private static final long serialVersionUID =
1173984872572414699L;
//头节点
private transient Node firstWaiter;
//尾节点
private transient Node lastWaiter;
public ConditionObject() {
}
/** 省略方法 **/
}
10.2.2 等待状态
10.2.3 通知
//检测当前线程是否为拥有锁的独
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//头节点,唤醒条件队列中的第一个节点
Node first = firstWaiter;
if (first != null)
doSignal(first); //唤醒
}
private void doSignal(Node first) {
do {
//修改头结点,完成旧头结点的移出工作
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}