这篇文章主要讲解了“如何理解Java并发之同步器设计”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解Java并发之同步器设计”吧! 前言: 在 Java并发内存模型详情了解到多进程(线程)读取共享资源的时候存在竞争条件。 计算机中通过设计同步器来协调进程(线程)之间执行顺序。 在 这些同步器在功能设计上有所不同,但是内部实现上有共通的地方。 1、同步器同步器的设计一般包含几个方面:状态变量设计(同步器内部状态),访问条件设定,状态更新,等待方式,通知策略。 访问条件是控制线程是否能执行(访问共享对象)的条件,它往往与状态变量紧密相关。而通知策略是线程释放锁定状态后通知其它等待线程的方式,一般有以下几种情况:
看下面例子,通过锁方式的同步器 public class Lock{ // 状态变量 isLocked private boolean isLocked = false; public synchronized void lock() throws InterruptedException{ // 访问条件 当isLocked=false 时获得访问权限否则等待 while(isLocked){ // 阻塞等待 wait(); } //状态更新 线程获得访问权限 isLocked = true; } public synchronized void unlock(){ //状态更新 线程释放访问权限 isLocked = false; // 通知策略 object.notify | object.notifyAll notify(); } } 我们用计数信号量控制同时执行操作活动数。这里模拟一个连接池。 public class PoolSemaphore { // 状态变量 actives 计数器 private int actives = 0; private int max; public PoolSemaphore(int max) { this.max = max; } public synchronized void acquire() throws InterruptedException { //访问条件 激活数小于最大限制时,获得访问权限否则等待 while (this.actives == max) wait(); //状态更新 线程获得访问权限 this.actives++; // 通知策略 object.notify | object.notifyAll this.notify(); } public synchronized void release() throws InterruptedException { //访问条件 激活数不为0时,获得访问权限否则等待 while (this.actives == 0) wait(); //状态更新 线程获得访问权限 this.actives--; // 通知策略 object.notify | object.notifyAll this.notify(); } } 1.1 原子指令同步器设计里面,最重要的操作逻辑是“如果满足条件,以更新状态变量来标志线程获得或释放访问权限”,该操作应具备原子性。 比如 function Lock(boolean *lock) { while (test_and_set(lock) == 1); } 另外还有很多原子指令 同步操作中,利用计算机原子指令,可以避开锁,提升效率。 看下面例子,主要在区别是等待方式不一样,上面是通过 public class Lock{ // 状态变量 isLocked private AtomicBoolean isLocked = new AtomicBoolean(false); public void lock() throws InterruptedException{ // 等待方式 变为自旋等待 while(!isLocked.compareAndSet(false, true)); //状态更新 线程获得访问权限 isLocked.set(true); } public synchronized void unlock(){ //状态更新 线程释放访问权限 isLocked.set(false); } } 1.2 关于阻塞扩展说明阻塞意味着需要将进程或线程状态进行转存,以便还原后恢复执行。这种操作是昂贵繁重,而线程基于进程之上相对比较轻量。线程的阻塞在不同编程平台实现方式也有所不同,像 在《Java Concurrency in Practice》中,作者提到
从上面可以看出JVM实现阻塞两种方式
JVM中通过 2、AQS
状态变量 是用 int getState()void setState(int newState)boolean compareAndSetState(int expect, int update) 该状态值在不同API中有不同表示意义。比如 关于等待方式和
它定义了两种资源共享方式。
每个节点包含
AQS 几个关键 API
2.1 acquire(int arg)public final void acquire(int arg) { if ( // 尝试直接去获取资源,如果成功则直接返回 !tryAcquire(arg) && //线程阻塞在同步队列等待获取资源。等待过程中被中断,则返回true,否则false acquireQueued( // 标记该线程为独占方式,并加入同步队列尾部。 addWaiter(Node.EXCLUSIVE), arg) ) selfInterrupt(); } 2.2 release(int arg)public final boolean release(int arg) { // 尝试释放资源 if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) // 唤醒下一个线程(后继节点) unparkSuccessor(h); return true; } return false; }private void unparkSuccessor(Node node) { .... Node s = node.next; // 找到后继节点 if (s == null || s.waitStatus > 0) {//无后继或节点已取消 s = null; // 找到有效的等待节点 for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); // 唤醒线程 } 感谢各位的阅读,以上就是“如何理解Java并发之同步器设计”的内容了,经过本文的学习后,相信大家对如何理解Java并发之同步器设计这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。 |
|