原子操作

volitale 怎么保证可见性

醉酒当歌 提交于 2020-01-20 04:53:51
VOLATILE 只保证可见性,并不保证原子性 在说明Java多线程内存可见性之前,先来简单了解一下Java内存模型。 (1)Java所有变量都存储在主内存中 (2)每个线程都有自己独立的工作内存,里面 存该线程的使用到的变量副本(该副本就是主内存中该变量的一份拷贝) (1)线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接在主内存中读写 (2)不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。 线程1对共享变量的修改,要想被线程2及时看到,必须经过如下2个过程: (1)把工作内存1中更新过的共享变量刷新到主内存中 (2)将主内存中最新的共享变量的值更新到工作内存2中 可见性与原子性 可见性:一个线程对共享变量的修改,更够及时的被其他线程看到 原子性:即不可再分了,不能分为多步操作。比如赋值或者return。比如"a = 1;"和 "return a;"这样的操作都具有原子性。类似"a += b"这样的操作不具有原子性,在某些JVM中"a += b"可能要经过这样三个步骤: ① 取出a和b ② 计算a+b ③ 将计算结果写入内存 Synchronized:保证可见性和原子性 Synchronized能够实现原子性和可见性,在Java内存模型中,synchronized规定,线程在加锁时,先清空工作内存

Java多线程(5)

风格不统一 提交于 2020-01-19 18:35:07
Java多线程(5) CPU缓存一致性问题 因为缓存的出现,极大提高了CPU的吞吐能力,但同时也引入了缓存不一致的问题,比如i++操作 在程序运行过程,首先将主存中的数据复制一份存放到CPU Cache中,那么CPU寄存器进行数值计算的时候就直接到Cache中读取和写入,当整个运算过程完毕之后再讲Cache中的数据刷新到主存当中 具体如下: 读取主内存的i到cpu cache 对i进行+1操作 将结果写回到cpu cache中 将数据刷新到主内存 i++在单线程环境不会有什么问题,但在多线程下就会出现问题了 每个线程都有自己的工作内存,变量i会在多个线程的本地内存中都保存一个副本,如果同时两个线程执行i++操作,假设i的初始值为0,每一个线程都从主内存中获取i的值存入cpu cache,然后经过计算再写入主内存,很有可能i在经过了两次自增之后结果还是1,这就是典型的缓存不一致的问题 Java内存模型决定了一个线程对共享变量的写入何时对其他线程可见,Java内存模型定义了线程和主内存之间的抽象关系: 共享变量存储在主内存,每个线程都可以访问 每个线程都有私有的工作内存或者称为本地内存 工作内存只存储该线程对共享变量的副本 线程不能直接操作主内存,只有先操作了工作内存之后才能写入主内存 并发编程的三个重要特性 原子性 指在一次的操作或者多次操作中

LISP之根源

被刻印的时光 ゝ 提交于 2020-01-18 12:47:47
约翰麦卡锡于1960年发表了一篇非凡的论文,他在这篇论文中对编程的贡献有如欧几里德对几何的贡献. 1 他向我们展示了,在只给定几个简单的操作符和一个表示函数的记号的基础上, 如何构造出一个完整的编程语言. 麦卡锡称这种语言为Lisp, 意为List Processing, 因为他的主要思想之一是用一种简单的数据结构表(list)来代表代码和数据. 值得注意的是,麦卡锡所作的发现,不仅是计算机史上划时代的大事, 而且是一种在我们这个时代编程越来越趋向的模式.我认为目前为止只有两种真正干净利落, 始终如一的编程模式:C语言模式和Lisp语言模式.此二者就象两座高地, 在它们中间是尤如沼泽的低地.随着计算机变得越来越强大,新开发的语言一直在坚定地趋向于Lisp模式. 二十年来,开发新编程语言的一个流行的秘决是,取C语言的计算模式,逐渐地往上加Lisp模式的特性,例如运行时类型和无用单元收集. 在这篇文章中我尽可能用最简单的术语来解释约翰麦卡锡所做的发现. 关键是我们不仅要学习某个人四十年前得出的有趣理论结果, 而且展示编程语言的发展方向. Lisp的不同寻常之处--也就是它优质的定义--是它能够自己来编写自己. 为了理解约翰麦卡锡所表述的这个特点,我们将追溯他的步伐,并将他的数学标记转换成能够运行的Common Lisp代码. 七个原始操作符 开始我们先定义 表达式 .表达式或是一个

【整理】LISP简介

浪子不回头ぞ 提交于 2020-01-18 12:47:36
张老师一直强调AutoCAD的开发有3种接口,vba,lisp,objectarx。objectarx功能强大,但学起来比较难。而vba和lisp就相对简单了。而且到时候用objectarx作出来的程序可以轻易调用lisp,所以只要做好lisp的话,也是可以最后汇总到我们的程序里面的。他希望项目组的其它同学能够使用Visual Lisp,参与到项目的开发中。于是我特地在网上百度了一下,收集整理了一些关于Lisp的信息。信息主要来源于百度百科的数个网页。 LISP的历史 LISP(全名LIST Processor,即链表处理语言),由约翰·麦卡锡在1960年左右创造的一种基于 λ演算 的函数式编程语言。 Lisp 代表 LISt Processing,即表处理,这种编程语言用来处理由括号(即“(”和“)”)构成的列表。约翰麦卡锡于1960年发表了一篇非凡的论文,他在这篇论文中对编程的贡献有如欧几里德对几何的贡献.[1] 他向我们展示了,在只给定几个简单的操作符和一个表示函数的记号的基础上, 如何构造出一个完整的编程语言. 麦卡锡称这种语言为Lisp, 意为List Processing, 因为他的主要思想之一是用一种简单的数据结构表(list)来代表代码和数据. 值得注意的是,麦卡锡所作的发现,不仅是计算机史上划时代的大事, 而且是一种在我们这个时代编程越来越趋向的模式

MySQL事务的实现原理

[亡魂溺海] 提交于 2020-01-17 21:38:58
特点 原子性(Atomicity),一致性(Consistency),隔离型(Isolation)以及持久性(Durability) 一、事务的目的 1、可靠性和并发处理 可靠性:数据库要保证当insert或update操作时抛异常或者数据库crash的时候需要保障数据的操作前后的一致,想要做到这个,我需要知道我修改之前和修改之后的状态,所以就有了undo log和redo log。 并发处理:也就是说当多个并发请求过来,并且其中有一个请求是对数据修改操作的时候会有影响,为了避免读到脏数据,所以需要对事务之间的读写进行隔离,至于隔离到啥程度得看业务系统的场景了,实现这个就得用MySQL 的隔离级别。 二、实现事务功能的三个技术 1、日志文件(redo log 和 undo log) 2、锁技术 3、MVCC 1.1 redo log 与 undo log介绍 1.1.1redo log 什么是redo log ? redo log叫做重做日志,是用来实现事务的持久性。该日志文件由两部分组成:重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。 当事务提交之后会把所有修改信息都会存到该日志中。假设有个表叫做tb1(id,username) 现在要插入数据(3,ceshi) start transaction; select

Java并发--原子变量、CAS算法

老子叫甜甜 提交于 2020-01-17 01:36:28
并发编程中的原子性问题 测试:20个线程对一个普通int变量进行++操作。 package pers . zhang . juc . part1 ; /** * @author zhang * @date 2020/1/16 - 17:52 * * 并发编程中的原子性问题 */ public class TestAtomicDemo { public static void main ( String [ ] args ) { AtomicDemo ad = new AtomicDemo ( ) ; for ( int i = 0 ; i < 20 ; i ++ ) { new Thread ( ad , "thread" + i ) . start ( ) ; } } } class AtomicDemo implements Runnable { private int serialNumber = 0 ; @Override public void run ( ) { try { Thread . sleep ( 200 ) ; } catch ( InterruptedException e ) { e . printStackTrace ( ) ; } System . out . println ( Thread . currentThread ( ) .

利用Redis原子计数器incr实现计数器及接口限流

流过昼夜 提交于 2020-01-15 05:15:29
一、INCR命令介绍 Redis Incr 命令将 key 中储存的数字值增一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。且将key的有效时间设置为长期有效 。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。 因为Redis没有专用的整数类型,所以在内存中是以字符串的形式存储的 二、常见使用场景 1、计数 我们可能常会统计网站页面每天访问量,通过incr命令在redis中设置key,每次增加1,设置24小时过期。 2、限流 日常的开放平台API一般常有限流,利用redis的incr命令可以实现一般的限流操作。如限制某接口每分钟请求次数上限1000次 /** * 60秒内最大1000次 * @param key 可以设计为用户标识及接口标识组合 * @param expireMillis 过期时间60s * @return */ public Boolean limiter(String key, Long expireMillis) { Long count = redisTemplate.opsForValue().increment(key, INCREMENT_STEP); if (1 == count) { redisTemplate

线程安全的原子性、可见性与有序性

本秂侑毒 提交于 2020-01-15 04:16:36
原子性: 一个或多个操作在CPU执行过程中不被中断的特性称之为原子性 。线程中执行的操作要么全部执行,要么全部不执行。 Java内存模型中的read、load、assign、use、store和write都可以保证原子性的操作(如果对Java内存模型不熟悉,可以参考我的这篇博文 Java内存模型 ),一般基本数据类型访问读写都是原子性的(long和double的非原子性协议例外,但是基本也不会发生) 如果应用场景需要一个更大范围的原子性保证,我们可以直接使用synchronized关键字,在synchronized块之间的操作也具有原子性。 可见性: 可见性是指当一个线程修改了共享变量的值,其他线程能够立即得知这个修改。 Java内存模型是通过变量修改之后同步回主内存,在变量读取之前从主内存刷新变量值来实现可见性的。如果一个普通变量在多线程中运行的话,往往不能保证可见性,多个线程都是从主内存中获取变量,这时多个线程并不会先等待某一个线程修改完变量之后,写回主内存,再从主内存获取最新的变量值。 如果要保证可见性的话,我们可以使用volatile关键字,具体原理可以参考我的这篇博文 Java的volatile关键字 ,当然除了volatile关键字之外,synchronized和final关键字也能实现可见性

Redis>使用总结(优缺点)

被刻印的时光 ゝ 提交于 2020-01-14 18:38:23
优点 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。 缺点 1.作为非关系型数据库,不支持条件查询 来源: CSDN 作者: arize 链接: https://blog.csdn.net/qq_42069216/article/details/103974196

CAS及ABA问题

╄→гoц情女王★ 提交于 2020-01-13 05:25:04
CAS 并发中线程安全必须保证三个要素, 原子性、可见性、有序性 。使用volatile可以保证可见性和有序性,但是不能保证原子性。所以还是会出现并发修改紊乱的问题。 这里的解决方法可以通过synchronized修饰,但是太重了,所以使用原子类保证原子性即可,但是原子类底层是如何保证原子性的值得研究,首先一点就是CAS。 CAS是什么 campareAndSwap. JDK8,在AtomicInteger类中有一个getAndAdd()方法,点进去,可以看到: public class AtomicInteger extends Number implements java . io . Serializable { private static final long serialVersionUID = 6214790243416807050 L ; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe . getUnsafe ( ) ; private static final long valueOffset ; static { try { valueOffset = unsafe . objectFieldOffset (