2.2.3.复合操作(Compound Actions ) LazyInitRace和UnsafeCountingFactorizer两个都包含了一系列需要在同一个状态上相对于其他操作保证原子性或者不可分割性的操作 原子操作是指,对于访问同一个状态的所有操作(包括该操作本身)来说,这个操作是一个以原子方式执行的操作。 (像递增)必须要是原子的。 我们把check-then-act以及read-modify-write一系列统一称之为compound actions,就是复合操作,或者叫组合操作:为了确保线程安全性,一系列的操作的执行都必须是原子操作 在后面的译文中,我们将会介绍锁机制,那种java内建的确保原子性的机制。现在的话,我们就暂时通过现存的线程安全类来修复问题吧,就像程序清单2.4里的CountingFactorizer那样。
java实现原子性 java.util.concurrent.atomic 包中提供了很多高级的指令,来保证操作的原子性 Atomiclnteger 类提供了方法 incrementAndGet 用来自增 AtomicLong nextNumber = new AtomicLong(); long id = nextNumber.incrementAndGet() incrementAndGet 方法以原子方式将
no ok //如果期望值是2,则修改成8 count.compareAndSet(2, 8); //ok System.out.println(count.get unsafe.getAndSetObject(this, valueOffset, newValue); } } compareAndSet采用CAS保证并发 AtomicReference 所提供的某些方法可以进行原子性操作 ,如compareAndSet、getAndSet,这仅仅是对引用进行原子性操作 AtomicReference 不能保证对象中若存在属性值修改是线程安全的,如假设引用对象是person,修改person AtomicintegerFieldUpdater安全的修改自定义对象 atomic包中提供AtomicReferenceFieldUpdater、AtomicIntegerFieldUpdater、AtomicLongFieldUpdater,原子性的更新某一个类实例的指定的某一个字段 AtomicIntegerFieldUpdaterImpl (tclass, fieldName, Reflection.getCallerClass()); } /** * 原子性设置
Atomic原子操作类提供了一种用法简单, 性能高效, 线程安全的变量更新方式. 今天就以AtomicInteger为例,看看它是如何做到的. 值的封装是用volatile修饰的. private volatile int value; 在之前的浅析volatile文章中, 我们已经了解了, volatile在多线程中, 仅可以保证可见性, 保证不了原子性的 相同在更新为update, 否则更新失败; 这也是CAS的基本处理流程. public final native boolean compareAndSwapInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(! this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; } 逻辑很简单, 利用CAS机制, 将value值更新为计算后的值
fmt.Println("ops:", opsFinal) // var tmpOps int64 = 0 t1 := time.Now().UnixNano() //执行n个线程执行原子操作 tmpOps < 10000000 { go func() { atomic.AddInt64(&tmpOps, 1) }() } t2 := time.Now().UnixNano() t := t2 - t1 fmt.Println(t) fmt.Println(tmpOps) }
类加载器子系统还承担着安全性的责任,并且是JVM的动态链接和动态加载的基础。将二进制信息=>类型信息的数据结构,中间需要经过很多步骤。 方法区的生命周期与JVM相同,被多个线程共享,所以要考虑并发访问的安全性的问题。JVM规范规定在需要的内存得不到满足的情况下,方法区会抛出OutOfMemoryException。 (2)根搜索,HotSpot虚拟机采用这种算法标记存活对象。把方法区、JVM栈中的所有的引用组成的集合作为搜索的根,从这个集合开始遍历直到结束。 (2)标记清扫,先进行存活对象标记,回收时将“死”对象占用的内存直接释放掉,会产生大量的内存碎片。 但是在JDK1.5,Sun提供了CMS(Concurrent Mark and Sweep)垃圾收集器,通过GC线程和用户线程并发执行减少GC时间,提高了JVM的实时性。
原子性布尔 AtomicBoolean AtomicBoolean 类为我们提供了一个可以用原子方式进行读和写的布尔值,它还拥有一些先进的原子性操作,比如 compareAndSet()。 2. 原子性整型 AtomicInteger AtomicInteger 类为我们提供了一个可以进行原子性读和写操作的 int 变量,它还包含一系列先进的原子性操作,比如 compareAndSet()。 原子性长整型 AtomicLong AtomicLong 类为我们提供了一个可以进行原子性读和写操作的 long 变量,它还包含一系列先进的原子性操作,比如 compareAndSet()AtomicLong 原子性引用型 AtomicReference AtomicReference 提供了一个可以被原子性读和写的对象引用变量。
ZAB被称为原子广播协议,也是做了这一层封装,即:multi命令。 #pRequestHelper,我省略掉了try-catch和其他无关代码,在处理multi请求时,ZooKeeper会先遍历multiRequest,把每个元素当做一个单独的提案调用pRequest2Txn MultiOperationRecord multiRequest = new MultiOperationRecord(); try { ByteBufferInputStream.byteBuffer2Record OpCode.error; txn = new ErrorTxn(Code.RUNTIMEINCONSISTENCY.intValue()); } else { try { pRequest2Txn
关于概念: 原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 为了实现这一点,线程安全方法必须是原子的,例如,其他线程只能看到方法之前或之后调用之间的状态。 以下示例说明了为什么线程安全方法必须是原子的: public class TR extends FanLibrary { private volatile int i = 0; public ,代码中sleep(100)的原因: INFO-> beforeINFO-> 1INFO-> after 其中“i++;”相当于“i = i + 1;”包含了“i + 1”和“i =”两个过程,不属于原子操作
最传统的,正在写文件的进程加锁,其他进程等待,但是这样的情况是绝对不允许的,效率太过低下 2. ,这样内核保证了写入数据的完整性,但是不保证写入的原子性。 管道 SUS 标准对管道写入有着明确的说明,只要一次性写入数据小于管道缓冲区长度(PIPE_BUF),那么不论 O_NONBLOCK 标识是否开启,管道写入都是原子性的,多个进程同时写入同一管道是一定不会出现数据交错的 也许操作系统设计者认为,socket 是有可能永久阻塞的,所以如果保证这样的 IO 具备原子性是十分荒唐的一件事吧。 原子性的可靠性 那么问题来了,nginx 直接调用 write,这样靠谱吗?
这篇文章,我们将给大家来讲解引起我们并发问题的三大因素--— 有序性、可见性、原子性。这三个问题是属于并发领域的所以并不涉及语言。 首先,我们来聊聊什么是安全性。 比如: private Integer sum(int num) { return ++num; } ---- 原子性 说到原子性,可能大家的第一反应就是 i++ 和 ++i 了。 1,而不是我们期望的 2。 有序性 在我们依赖的 jvm中,还有个更坑的问题,就是有序性,也就是指定重排序。 易失性变量规则:在对该相同字段的每次后续读取之前发生对易失性字段的写入。易失性字段的写入和读取具有与进入和退出监视器(读取和写入时的同步块)类似的内存一致性效果,但实际上没有获取监视器/锁定。
JAVA的原子性、可见性与有序性了解一波。 原子性(Atomicity):一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 我们先来看看哪些是原子操作,哪些不是原子操作,先有一个直观的印象: int k = 5; //代码1 k++; //代码2int j = k; //代码3k = k + 具体到底层的指令(上文内存间操作提到的8个指令),由Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write,我们大致可以认为基本数据类型的访问读写是具备原子性的 和monitorexit来隐式地使用这两个操作,这两个字节码指令反映到Java代码中就是同步块——synchronized关键字,因此在synchronized块之间的操作也具备原子性。 我们先看下以下的例子,对有序性有一个直观的印象: 例子1: int k = 0; int j = 1;k = 5; //代码1j = 6; //代码2 按照有序性的规定,该例子中的代码1应该在代码2之前执行
原子性:undo log 事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部失败回滚。 undo log记录了回滚操作的日志,如果要撤销,按照undo log的回滚日志执行一遍就可以了(保证了原子性) 持久性:redo log 指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的, 持久性就是用redo log,不是每次都写入磁盘,而是定期通过redo log把数据刷入磁盘这样即便断电后,重启mysql还是可以恢复
上一次我们说到了可见性,原子性,有序性,今天我们看看如何解决这个问题,今天我们先看看可见性和有序性,因此我们先要知道java内存模型 什么是java内存模型 我们上一节已经知道,可见性是由于缓存导致,有序性是由于编译优化导致 } } } 程序的顺序性 按照程序的顺序,前面的操作happen-before后面的任意操作,比如上面的例子第6行x=42,Happens-before第7行v=true,规则一比较好理解,程序前面的修改对后面的操作是可见 volatile变量的写操作,hanppens-before后面操作的读操作.这样看起来和1.5版本的含义没有区别呀,但看这样就是禁止缓存的,但是你在看第三条规则,就会明白,1.5版本之后的增强 传递性 对上一段代码讲解一下 X=42happens-before 写变量v=true,规则1 写v=true happens-before读变量v=true,规则2 在根据传递性,则x=42 happens-before
原子性 原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 看下面一个例子: 请分析以下哪些操作是原子性操作: 1 x = 10; //语句1 2 y = x; //语句2 3 x++; //语句3 4 x = x + 1; //语句4 咋一看,有些朋友可能会说上面的 4个语句中的操作都是原子性操作。 其实只有语句1是原子性操作,其他三个语句都不是原子性操作。 语句1是直接将数值10赋值给x,也就是说线程执行这个语句的会直接将数值10写入到工作内存中。 语句2实际上包含2个操作,它先要去读取x的值,再将x的值写入工作内存,虽然读取x的值以及 将x的值写入工作内存 这2个操作都是原子性操作,但是合起来就不是原子性操作了。
首先要了解的是,volatile可以保证可见性和顺序性,这些都很好理解,那么它为什么不能保证原子性呢? 原子性 问题来了,既然它可以保证修改的值立即能更新到主存,其他线程也会捕捉到被修改后的值,那么为什么不能保证原子性呢? j,两个原子操作加起来就不是原子操作了。 所以,如果一个变量被volatile修饰了,那么肯定可以保证每次读取这个变量值的时候得到的值是最新的,但是一旦需要对变量进行自增这样的非原子操作,就不会保证这个变量的原子性了。 volatile具有可见性,也不能保证对它修饰的变量具有原子性。
依靠原子性而不是使用锁是否安全? 首先,上面的链接FAQ并没有说明这种行为多大程度上被认为是Python规范的一部分,还是CPython实现的情况。 我当然可以想象有些优化可能会使这些操作的原子性无效。 其次,即使不是绝对必要的,锁也提供了明确的线程安全保证,并且可以作为代码访问共享内存的有用说明。 第三,因为Python允许重载如此多的内建方法,所以有些情况下这些操作不再是原子的。 Google Python风格指南建议: 不要依赖于内置类型的原子性。 虽然Python的内置数据类型(如字典)似乎具有原子操作,但是在某些情况下它们不是原子的(例如,如果将hash或eq实现为Python方法),并且不应该依赖它们的原子性。 依靠操作的原子性有效地允许您在GIL上搭载锁定,从而降低额外锁的成本。 但是,如果锁的性能如此重要,你最好首先分析热点并寻找其他加速点。
将这两个指令打包成lua脚本,便可实现原子性操作。
原子性 mysql原子性利用了undo log。 记录回滚的日志信息,利用undo log中的信息将数据回滚到修改之前。 持久性 mysql持久性的利用了redo log。 一致性 mysql一致性的保证是从2个方面来保证。 一方面数据库层面A(原子性)、I(隔离性)、D(持久性)保证了一致性。另一方面符合约束的业务代码保证了一致性。
正文 原子性 原子性是指一个操作是不可中断的,即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其他的线程干扰。 一般认为CPU的指令都是原子操作,但是我们写的代码就不一定是原子操作了。 故线程切换造成的原子性问题。 有序性 在并发时,由于编译器重排序导致不是按照程序的顺序执行。 3 但是CPU重排序之后执行顺序可能变成了 1-3-2 步骤如下: A首先进入synchronized,由于instance为null,所以它执行instance = new SingletonDemo 这样就永远得不到结果2。 类推的话,循环执行10000次也是同理,线程A执行+1操作时不能及时获得线程B已经写入的值,故导致值永远不可能达到20000。