- 浏览: 378621 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
真的全站唯一:
描述的能不能准确一点,我也以为bigDecimal性能比dou ...
【性能】Java BigDecimal和double性能比较 -
zhanggang807:
学习到了。。以后会考虑往这方面设计
【java规范】Java spi机制浅谈 -
Xiong506:
xiyuan1025 写道你这是在linux下吗,我在linu ...
[监控]Btrace监控简单笔记 -
Xiong506:
xiyuan1025 写道你这是在linux下吗,我在linu ...
[监控]Btrace监控简单笔记 -
Bll:
找不到实现类
【java规范】Java spi机制浅谈
synchronized的基本原理回顾
在jvm内部,所有对象都含有单一的锁,jvm负责跟踪监视被加锁次数,叫做对象监视器。当线程第一次给对象加锁的时候,计数器会加1,离开时会减1.同样任务是可重入的,每次重入也是加1,离开减1.
synchronized是独占式的,拿到对象锁才能继续,没有获取到锁就会阻塞。
JUC CAS乐观锁基本原理
synchronized就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
乐观锁就是假设没有冲突而去完成某项操作,如果因为冲突失败就重试。
原理有点类似于在数据库记录字段增加一个版本号,每次更新的时候做两个事情:
1.检查数据库记录当前的版本号和读取到的版本号是否一致,不一致说明数据已不是最新,更新失败需要重试.
2.如果版本号一致,更新成功,同时将版本号加1.
在jdk1.5开始,Doug Lea在JUC类库里提供了类似的乐观锁的机制叫CAS.
CAS简介:compareAndSet的意思,就是先比较是否是期望值(是期望值,说明没人更改过,当然也有可能有ABA情况),如果是再设值,不是就设值失败,线程阻塞。如果是基于这个,就要保证比较和设值这两个动作是原子性的,如何保证呢?这个是借助于JNI,利用CPU硬件支持来完成的。利用硬件提供swap和test_and_set指令,单CPU下同一指令的多个指令周期不可中断,SMP中通过锁总线支持这两个指令的原子性。
volilate关键字
Java语言规范允许线程保存共享成员变量的私有拷贝,当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。这样就延伸出可见性的一个问题。
CAS解决了比较和更新的原子性,但是还有另外一个问题就是要保证可见性。Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
volatile关键字有两层含义:
1.对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。
2.volatile前后的代码不能重排
其他话题
volilate和cas只能乐观锁保证的状态控制的正确,而在设置状态失败的时候,仍然需要阻塞线程。juc里提供了LockSupport的park和unpark方法用于阻塞线程。而不同的场景下需要不同的等待策略和锁共享策略,juc提供了AbstractQueuedSynchronizer(AQS)为基类的一序列不同的锁,底层都是基于CAS、LocakSupport和Queue来管理,后续有时间细细分析。
juc基于的CAS,提供了带有原子性的基本类型封装类,如AtomicInteger、AtomicLong等。
AtomicInteger原理:
如自增:
/** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) //cas return current; } }
compareAndSet的实现如下:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); //JNI调用 }
自己实现简单乐观独占锁
基于cas,本人简单实现了一个乐观独占锁,代码如下:
基于CAS简单乐观独占锁
package lock.test; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; /** * 简单乐观独占锁 */ public class OptimisticExclusiveLock { /** * 独占锁标记 true 锁不可用 false 锁可用 */ private AtomicBoolean state = new AtomicBoolean(false); List<Thread> queue = new ArrayList<Thread>();//阻塞队列 public boolean lock() { if (!state.get()&&state.compareAndSet(false, true)) {//取锁成功不会阻塞,程序会继续执行 return true; // 利用CAS } else { queue.add(Thread.currentThread());//加入阻塞队列 LockSupport.park();//阻塞线程 return false; } } public boolean unLock() { if (state.get()) { queue.remove(Thread.currentThread());//从队列里移除 if (state.compareAndSet(true, false)) {// 利用CAS if(!queue.isEmpty()){ LockSupport.unpark(queue.get(0));//唤醒第一个等待线程 } return true; } return false; } else { return false; } } }
简单乐观独占锁测试
package lock.test; /** * @author Administrator 独占锁测试 */ public class OptimisticExclusiveLockTest { public static OptimisticExclusiveLock lock = new OptimisticExclusiveLock(); // 独占锁 public static volatile int i = 0; // 保证可见性 public class Task implements Runnable { @Override public void run() { while (true) { try { lock.lock();//加锁 i += 2; System.out.println("thread name:" + Thread.currentThread().getName() + " i=" + i); } finally { lock.unLock();//释放锁 try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } public void runTask() { for (int i = 0; i < 100; i++) { Thread t = new Thread(new Task(), "thread" + i); t.start(); } } public static void main(String[] args) { OptimisticExclusiveLockTest test = new OptimisticExclusiveLockTest(); test.runTask(); } }
这里实现的简单乐观独占锁很简单,但是能保证并发性。
JUC里面基于CAS实现了很多的锁,主要是基于AQS实现,如ReentrantLock,CountDownLatch,Semaphore,FutureTask等,适用于不同的锁场景。
评论
假设如下情况:
线程A占有了锁时,
当线程B尝试lock时执行到22,23行之间的时候(在queue.add(Thread.currentThread());这句前)
恰好之前的线程A此时释放了锁,因为此时线程B还没有把自己加入队列中,所以线程A执行到 LockSupport.unpark(queue.get(0));这句的时候并没有唤醒任何线程。
所以当线程A的unlock执行完毕后,有可能线程B仍然处于等待状态。
你好,你说得是对的,还需要对队列的所有操作都加一个锁。
等待队列不能简单的使用一个ArrayList,真正在实现juc的AQS里是使用一个CHL队列,该队列非阻塞,且没有这个问题。
本文是为了探讨java乐观锁的实现机理,例子可能有不完美之处,谢谢提示。
假设ArrayList改成了线程安全的实现后
再将lock方法改成如下,是否可以解决问题?
public boolean lock(){
if (!state.get()&&state.compareAndSet(false, true)) {//取锁成功不会阻塞,程序会继续执行
return true; // 利用CAS
} else {
do{
if(state.get()){
queue.add(Thread.currentThread());//加入阻塞队列
LockSupport.park();//阻塞线程
}
}while(state.compareAndSet(true, true));
return false;
}
}
假设如下情况:
线程A占有了锁时,
当线程B尝试lock时执行到22,23行之间的时候(在queue.add(Thread.currentThread());这句前)
恰好之前的线程A此时释放了锁,因为此时线程B还没有把自己加入队列中,所以线程A执行到 LockSupport.unpark(queue.get(0));这句的时候并没有唤醒任何线程。
所以当线程A的unlock执行完毕后,有可能线程B仍然处于等待状态。
你好,你说得是对的,还需要对队列的所有操作都加一个锁。
等待队列不能简单的使用一个ArrayList,真正在实现juc的AQS里是使用一个CHL队列,该队列非阻塞,且没有这个问题。
本文是为了探讨java乐观锁的实现机理,例子可能有不完美之处,谢谢提示。
假设如下情况:
线程A占有了锁时,
当线程B尝试lock时执行到22,23行之间的时候(在queue.add(Thread.currentThread());这句前)
恰好之前的线程A此时释放了锁,因为此时线程B还没有把自己加入队列中,所以线程A执行到 LockSupport.unpark(queue.get(0));这句的时候并没有唤醒任何线程。
所以当线程A的unlock执行完毕后,有可能线程B仍然处于等待状态。
有个疑问,在基于CAS简单乐观独占锁简单实现里,采用ArrayList做阻塞队列,会有线程安全问题的,因为unlcok实现里queue.isEmpty()和唤醒阻塞队列不是原子操作,可能导致阻塞的队列无法被唤醒。
提醒的是 实验不够严谨 arraylist可以换成vector
有个疑问,在基于CAS简单乐观独占锁简单实现里,采用ArrayList做阻塞队列,会有线程安全问题的,因为unlcok实现里queue.isEmpty()和唤醒阻塞队列不是原子操作,可能导致阻塞的队列无法被唤醒。
发表评论
-
Xml ResourceBundle简单实现
2012-04-17 21:45 4468ResourceBundle主要是用于和本地语言环境相关的一些 ... -
【maven】多子模块maven模板工程archetype创建过程
2012-04-02 20:55 17662最近项目里需要创建一 ... -
【java基础】如何设计java应用程序的平滑停止
2012-03-05 23:44 11019java应用程序退出的触发机制有: 1.自动结束:应用没有存 ... -
【java并发】juc Executor框架详解
2012-02-26 13:55 12509Executor 框架是 juc 里提供的线程池的实现。 ... -
【java并发】juc高级锁机制探讨
2012-02-23 00:52 8718最近在看一些j ... -
[NoSQL]MongoDB初体验
2012-01-05 16:06 3969因为未来业务发展的一 ... -
【JVM】HotSpot JVM内存管理和GC策略总结
2011-12-13 22:05 15969JVM的相关知识是学习java ... -
32位机器下的一个java.lang.OutOfMemoryError错误分析
2011-10-17 11:19 2598昨天在本人windows机器( ... -
[监控]Btrace监控简单笔记
2011-09-09 10:57 5063前阵子看了公司网站的一个cache 命中率统计的btrace监 ... -
DBCP数据源配置项记录
2011-09-01 20:22 3000网站最近发生了数据库连接爆掉的问题。排查了下各个应用存在 ... -
【性能】Java BigDecimal和double性能比较
2011-08-28 20:06 14257我们知道 java 里面有个 BigDecimal ... -
【Spring】IOC容器并发条件下,可能发生死锁
2011-08-28 17:07 69411.背景 上周在生产环境应用启 ... -
JDK7 AIO 初体验
2011-08-17 19:20 2605JDK7 AIO初体验 JDK7已经releas ... -
如果要用java实现算法,一定慎用递归
2011-04-06 20:41 12962现象 : 递归是我们很经典的一种算法实现,可以很好的 ... -
java日志,需要知道的几件事(commons-logging,log4j,slf4j,logback)
2011-02-28 17:12 46410java日志,需要知道的几件事 如果对于comm ... -
JVM问题诊断常用命令:jinfo,jmap,jstack
2010-08-17 17:55 125011.jinfo 描述:输出给定 java ... -
java 浮点数为什么精度会丢失
2010-07-15 22:30 4924由于对float或double 的使用不当,可能会出现精度 ... -
一个枚举类的方法设计
2010-06-21 15:28 1691public enum ActionType { A ... -
java内部字符编码浅析
2010-06-07 21:43 6860java内部字符编码浅析 本周遇到一个 ... -
JDK反射之JDK动态proxy
2010-06-07 21:27 4125JDK动态代理 JDK 动态代理是 java 反射的一 ...
相关推荐
比如Lock接口及其实现ReentrantLock,提供了比synchronized更灵活的锁机制,同时AQS(AbstractQueuedSynchronizer)是实现各种锁的基础同步器,它管理着一系列的等待线程,并提供一种框架来实现独占锁和共享锁。...
Java 锁可以分为独占锁和共享锁两种,独占锁只能被一个线程持有,共享锁可以被多个线程持有。 公平锁非公平锁 公平锁和非公平锁是 Java 中的两种锁机制,公平锁按照线程的请求顺序提供锁,非公平锁按照线程的请求...
JUC提供了多种锁的实现,包括公平锁和非公平锁、可重入锁、自旋锁、独占锁、共享锁等。锁的选择和使用极大地影响着并发性能。 #### 同步器 JUC的同步器包括CountDownLatch、CyclicBarrier、Semaphore等,它们是为了...
Java并发编程是提升系统性能和资源利用率的关键技术之一,JUC(Java Concurrency Utilities)是Java平台中的并发工具包,提供了丰富的并发控制和同步机制。本文将深入探讨JUC中的多线程及高并发相关知识点。 一、...
AQS作为Java并发工具包(JUC)中的一个核心抽象类,其设计目的是为了实现各种同步器(如锁、信号量等)。AQS主要通过三个核心组成部分来实现这些同步组件的功能: 1. **State变量及其CAS操作**:AQS维护了一个名为`...
Java并发编程是Java开发中的重要领域,而JUC(Java Util Concurrency)是Java平台提供的一套强大且全面的并发工具包,它位于`java.util.concurrent`包下。JUC为开发者提供了高效、线程安全的组件,使得多线程编程...
本文主要探讨的是Java并发库(Java Concurrency in Practice, JUC)中的公平锁,它遵循先来先服务(First-Come First-Served, FCFS)原则,保证了线程获取锁的公平性。 1. **AQS(AbstractQueuedSynchronizer)** ...
#### Java并发 1. **同步和互斥的区别** - 同步是指两个或多个进程(线程)之间相互配合的一种机制。 - 互斥是指多个线程同时访问一个资源时,保证只有一个线程可以访问该资源。 - 实现同步的常见方式有锁、条件...
- **写独占**:当有一个写锁时,其他读锁和写锁均不能获得。 - **内部结构**: - **Sync**:抽象同步器,管理读写锁的状态。 - **Node**:用来构建等待队列的节点。 - **主要方法**: - **readLock()**:获取读...