首页 > 编程 > Java > 正文

JUC (Java Util Concurrency) 基础内容概述

2019-11-08 01:10:05
字体:
来源:转载
供稿:网友

1. JUC概况

以下是java JUC包的主体结构:

Atomic : AtomicInteger Locks : Lock, Condition, ReadWriteLock Collections : Queue, ConcurrentMap Executer : Future, Callable, Executor Tools : CountDownLatch, CyclicBarrier, Semaphore

2. 原子操作

多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤,那么这个操作就是原子的。出现原因: synchronized的代价比较高。

以下以AtomicInteger为例:

int addAndGet(int delta):以原子方式将给定值与当前值相加。 实际上就是等于线程安全版本的i =i+delta操作。 boolean compareAndSet(int expect, int update):如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。 如果成功就返回true,否则返回false,并且不修改原值。int decrementAndGet():以原子方式将当前值减 1。 相当于线程安全版本的–i操作。 int getAndAdd(int delta):以原子方式将给定值与当前值相加。 相当于线程安全版本的t=i;i+=delta;return t;操作。int getAndDecrement():以原子方式将当前值减 1。 相当于线程安全版本的i–操作。 int getAndIncrement():以原子方式将当前值加 1。 相当于线程安全版本的i++操作。 int getAndSet(int newValue):以原子方式设置为给定值,并返回旧值。 相当于线程安全版本的t=i;i=newValue;return t;操作。int incrementAndGet():以原子方式将当前值加 1。 相当于线程安全版本的++i操作。

3. 指令重排

你的程序并不能总是保证符合CPU处理的特性。

要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。

多核CPU,大压力下,两个线程交替执行,x,y输出结果不确定。可能结果:%20

1%202%203%204%20x%20=0,%20y%20=1%20x%20=1,%20y%20=1%20x%20=1,%20y%20=0%20x%20=0,%20y%20=0%204.%20Happens-before法则:(Java 内存模型)%20

如果动作B要看到动作A的执行结果(无论A/B是否在同一个线程里面执行),那么A/B就需要满足happens-before关系。%20

Happens-before的几个规则:%20

PRogram%20order%20rule:同一个线程中的每个Action都happens-before于出现在其后的任何一个Action。%20Monitor%20lock%20rule:对一个监视器的解锁happens-before于每一个后续对同一个监视器的加锁。%20Volatile%20variable%20rule:对volatile字段的写入操作happens-before于每一个后续的同一个字段的读操作。%20Thread%20start%20rule:Thread.start()的调用会happens-before于启动线程里面的动作。%20Thread%20termination%20rule:Thread中的所有动作都happens-before于其他线程检查到此线程结束或者Thread.join()中返回或者Thread.isAlive()==false。Interruption%20rule:一个线程A调用另一个另一个线程B的interrupt()都happens-before于线程A发现B被A中断(B抛出异常或者A检测到B的isInterrupted()或者interrupted())。Finalizer%20rule:一个对象构造函数的结束happens-before与该对象的finalizer的开始%20Transitivity:如果A动作happens-before于B动作,而B动作happens-before与C动作,那么A动作happens-before于C动作。因为CPU是可以不按我们写代码的顺序执行内存的存取过程的,也就是指令会乱序或并行运行,%20只有上面的happens-before所规定的情况下,才保证顺序性。%20JMM的特性:%20

多个CPU之间的缓存也不保证实时同步;JMM不保证创建过程的原子性,读写并发时,可能看到不完整的对象。(so D-check)%20

volatile语义:%20

volatile实现了类似synchronized的语义,却又没有锁机制。它确保对%20 volatile字段的更新以可预见的方式告知其他的线程。%20

Java%20存储模型不会对volatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。%20volatile变量不会被缓存在寄存器中(只有拥有线程可见),每次总是从主存中读取volatile变量的结果。%20

ps:volatile并不能保证线程安全的,也就是说volatile字段的操作不是原子性的,volatile变量只能保证可见性。%20

5.%20CAS操作%20

Compare%20and%20Swap%20

CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。%20

实现简单的非阻塞算法:%20

1%202%203%204%205%206%207%208%209%2010%2011%2012%2013%2014%20privatevolatileintvalue;// 借助volatile原语,保证线程间的数据是可见的%20 %20publicfinalintget()%20{%20    returnvalue;%20}%20 %20publicfinalintincrementAndGet()%20{%20    for(;;)%20{%20        intcurrent%20=%20get();%20        intnext%20=%20current%20+1;%20        if(compareAndSet(current,%20next))%20            returnnext;%20    }//Spin自旋等待直到返为止置%20}%20

整个J.U.C都是建立在CAS之上的,对于synchronized阻塞算法,J.U.C在性能上有了很大的提升。会出现所谓的“ABA”问题%20

6.%20Lock%20锁%20

Synchronized属于独占锁,高并发时性能不高,JDK5以后开始用JNI实现更高效的锁操作。%20

Lock—->%20

ReentrantLock—->%20

ReentrantReadWriteLock.ReadLock%20/%20ReentrantReadWriteLock.writeLock%20

ReadWriteLock—->%20ReentrantReadWriteLock%20

LockSupport%20

Condition%20

方法名称作用void%20lock()获取锁。如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态。void%20lockInterruptibly()%20throws%20InterruptedException;如果当前线程未被中断,则获取锁。如果锁可用,则获取锁,并立即返回。Condition%20newCondition();返回绑定到此 Lock 实例的新 Condition实例boolean%20tryLock();仅在调用时锁为空闲状态才获取该锁boolean%20tryLock(long%20time,%20TimeUnit%20unit)%20throws%20InterruptedException;如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁void%20unlock();释放锁

PS%20:%20一般来说,获取锁和释放锁是成对儿的操作,这样可以避免死锁和资源的浪费。%20

注:在%20finally%20里面做释放锁的操作%20

7.%20AQS%20

锁机制实现的核心所在。AbstractQueuedSynchronizer是Lock/Executor实现的前提。%20

AQS实现:%20

基本的思想是表现为一个同步器,AQS支持下面两个操作:%20

acquire:%20

1%202%203%204%205%20while(synchronization%20state%20does%20not%20allow%20acquire){%20    enqueue%20current%20threadifnot%20already%20queued;%20    possibly%20block%20current%20thread;%20}%20dequeue%20current%20threadifit%20was%20queued;%20

release:%20

1%202%203%20update%20synchronization%20state;%20if(state%20may%20permit%20a%20blocked%20thread%20to%20acquire)%20    unlock%20one%20or%20more%20queued%20threads;%20

要支持这两个操作,需要实现的三个条件:%20

Atomically%20managing%20synchronization%20state(原子性操作同步器的状态位)%20Blocking%20and%20unblocking%20threads(阻塞和唤醒线程)%20Maintaining%20queues(维护一个有序的队列)%20Atomically%20managing%20synchronization%20state%20

使用一个32位整数来描述状态位:private%20volatile%20int%20state;%20对其进行CAS操作,确保值的正确性。%20

Blocking%20and%20unblocking%20threads%20

JDK%205.0以后利用JNI在LockSupport类中实现了线程的阻塞和唤醒。%20

LockSupport.park()%20//在当前线程中调用,导致线程阻塞LockSupport.park(Object)LockSupport.unpark(Thread)%20

Maintaining%20queues%20

在AQS中采用CHL列表来解决有序的队列的问题。(CHL=%20Craig,%20Landin,%20and%20Hagersten)%20

Node里面是什么结构?

WaitStatus –>节点的等待状态,一个节点可能位于以下几种状态:

CANCELLED = 1: 节点操作因为超时或者对应的线程被interrupt。节点不应该不留在此状态,一旦达到此状态将从CHL队列中踢出。 SIGNAL = -1: 节点的继任节点是(或者将要成为)BLOCKED状态(例如通过LockSupport.park()操作),因此一个节点一旦被释放(解锁)或者取消就需要唤醒(LockSupport.unpack())它的继任节点。CONDITION = -2:表明节点对应的线程因为不满足一个条件(Condition)而被阻塞。 0: 正常状态,新生的非CONDITION节点都是此状态。

非负值标识节点不需要被通知(唤醒)。队列管理操作:

入队enqueue:

采用CAS操作,每次比较尾结点是否一致,然后插入的到尾结点中。

1 2 3 do{     pred = tail; }while( !compareAndSet(pred,tail,node) );

出队dequeue:

1 2 while(pred.status != RELEASED) ;     head  = node;

加锁操作:%20

1%202%203%204%205%20public%20final%20void%20acquire(intarg)%20{%20    if(!tryAcquire(arg))%20        acquireQueued(addWaiter(Node.EXCLUSIVE),%20arg))%20    selfInterrupt();%20}%20

释放操作:%20

1%202%203%204%205%206%207%208%209%20public%20final%20boolean%20release(intarg)%20{%20    if(tryRelease(arg))%20{%20        Node%20h%20=%20head;%20        if(h%20!=null%20&&%20h.waitStatus%20!=0)%20            unparkSuccessor(h);%20        return%20true;%20    }%20    return%20false;%20}%20

The%20synchronizer%20framework%20provides%20a%20ConditionObject%20class%20for%20use%20by%20synchronizers%20that%20maintain%20exclusivesynchronization%20and%20conform%20to%20the%20Lock%20interface.%20 %20  ——%20Doug%20Lea《%20The%20java.util.concurrent%20Synchronizer%20Framework%20》

以下是AQS队列和Condition队列的出入结点的示意图,可以通过这几张图看出线程结点在两个队列中的出入关系和条件。%20

原文地址:https://my.oschina.net/lifany/blog/146699#OSC_h3_1
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表