atomic

并发编程三要素:原子性,有序性,可见性

佐手、 提交于 2020-04-24 13:51:30
并发编程三要素 **原子性:**一个不可再被分割的颗粒。原子性指的是一个或多个操作要么全部执行成功要么全部执行失败。 有序性: 程序执行的顺序按照代码的先后顺序执行。(处理器可能会对指令进行重排序) 可见性: 一个县城对共享变量的修改,另一个线程能够立刻看到。 一、原子性 线程切换会带来原子性的问题 int i = 1; // 原子操作 i++; // 非原子操作,从主内存读取 i 到线程工作内存,进行 +1,再把 i 写到主内存。 虽然读取和写入都是原子操作,但合起来就不属于原子操作,我们又叫这种为“复合操作”。 我们可以用synchronized 或 Lock 来把这个复合操作“变成”原子操作。 例子: //使用synchronized private synchronized void increase(){ i++; } //使用Lock private int i = 0; Lock mLock = new ReentrantLock(); private void increase() { mLock.lock(); try { i++; } finally{ mLock.unlock(); } } 这样我们就可以把这个一个方法看做一个整体,一个不可分割的整体。 除此之前,我们还可以用java.util.concurrent.atomic里的原子变量类

浅析CAS与AtomicInteger原子类

孤者浪人 提交于 2020-04-23 22:33:51
一:CAS简介 CAS:Compare And Swap(字面意思是比较与交换),JUC包中大量使用到了CAS,比如我们的atomic包下的原子类就是基于CAS来实现。区别于悲观锁synchronized,CAS是乐观锁的一种实现,在某些场合使用它可以提高我们的并发性能。 在CAS中,主要是涉及到三个操作数,所期盼的旧值、当前工作内存中的值、要更新的值,仅当所期盼的旧值等于当前值时,才会去更新新值。 二:CAS举例 比如当如下场景,由于i++是个复合操作,读取、自增、赋值三步操作,因此在多线程条件下我们需要保证i++操作的安全 public class CASTest { int i = 0 ; public void increment() { i ++ ; } } 解决办法有通过使用synchronized来解决,synchronized解决了并发编程的原子性,可见性,有序性。 public class CASTest { int i = 0 ; public synchronized void increment() { i ++ ; } } 但synchronized毕竟是悲观锁,尽管它后续进行了若干优化,引入了锁的膨胀升级措施,但是还是存在膨胀为重量级锁而导致阻塞问题,因此,我们可以使用基于CAS实现的原子类AtomicInteger来保证其原子性 public

java中的CAS乐观锁

╄→гoц情女王★ 提交于 2020-04-23 15:34:15
最近,总是听到同事在面试的时候问候选人java中的锁相关的知识,大部分同学在问到CAS的时候会有些一知半解; 1. 原子操作 说到原子操作,会想到数据库事务中的原子性,道理都差不多,指一行或多行代码要么都执行成功或失败。 比如:i++这行代码,在执行的过程中会分为三步去执行: 1.取出i的值; 2.将i的值+1; 3.将+1后的赋值给i; 在单线程的情况下,这种操作不会有问题,但是多线程的情况下呢: 出现了线程B的结果将线程A的结果覆盖的情况;那就可以说i++不是原子操作; 可以本地验证下是不是这样的: private static int count = 0; public static void add() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } count++; } public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(100); for(int i=0;i<100;i++){ new Thread(() -> { add(); countDownLatch

300 行代码带你搞懂 Java 多线程!

青春壹個敷衍的年華 提交于 2020-04-23 04:42:20
作者:永远在路上 https://blog.csdn.net/weixin_44104367/article/details/104481510 线程 线程的概念,百度是这样解释的: 线程(英语:Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 在Unix System V及SunOS中也被称为轻量进程(Lightweight Processes),但轻量进程更多指内核线程(Kernel Thread),而把用户线程(User Thread)称为线程。 1.1 线程与进程的区别 进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。 线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。 也就是,进程可以包含多个线程,而线程是程序执行的最小单位。推荐看下这篇: 通俗易懂的理解进程与线程 。 1.2 线程的状态 NEW:线程刚创建 RUNNABLE: 在JVM中正在运行的线程,其中运行状态可以有运行中RUNNING和READY两种状态,由系统调度进行状态改变。 BLOCKED:线程处于阻塞状态,等待监视锁,可以重新进行同步代码块中执行 WAITING

【MySQL】:事务四大特性与隔离级别

落花浮王杯 提交于 2020-04-22 06:04:33
[toc] 一、事务的概念 什么是事务呢? 事务是由一步或几步数据库操作序列组成的逻辑执行单元,这系列操作 要么全部执行,要么全部放弃执行 。 二、事务的四大特性 原子性(Atomic),一致性(Consistency),隔离性(Isolation),持续性(Durability),简称ACID性。 1、原子性 原子性在多线程的时候学习过,通常表示不可再分的操作,表示 事务是应用中最小的执行单位 。 2、一致性 事务操作前后,数据总量不变 。如A像B转账500,A得减少500,B得加上500,这样才算是保证了数据库的一致性。如果A减了,B没加上去,这不是耍流氓莫。 一致性是通过原子性来保证的。 3、隔离性 各个事务的执行 互不干扰 ,任意一个事务的内部操作对其他并发的事务都是隔离的。 4、持续性 当事务提交或回滚后,数据库将会 持久化地保存数据 。 三、事务语句 数据库的语句由下列语句组成: 一组DML语句。 一条DDL语句。 一条DCL语句。 模拟转账: CREATE TABLE account( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(10), balance DOUBLE ); INSERT INTO account (NAME,balance) VALUES ('张三',1000),('李四',1000); 转账成功后

J.U.C剖析与解读1(Lock的实现)

假如想象 提交于 2020-04-22 05:31:01
J.U.C剖析与解读1(Lock的实现) 前言 为了节省各位的时间,我简单介绍一下这篇文章。这篇文章主要分为三块:Lock的实现,AQS的由来(通过演变的方式),JUC三大工具类的使用与原理剖析。 Lock的实现:简单介绍ReentrantLock,ReentrantReadWriteLock两种JUC下经典Lock的实现,并通过手写简化版的ReentrantLock和ReentrantReadWriteLock,从而了解其实现原理。 AQS的由来:通过对两个简化版Lock的多次迭代,从而获得AQS。并且最终的Lock实现了J.U.C下Lock接口,既可以使用我们演变出来的AQS,也可以对接JUC下的AQS。这样一方面可以帮助大家理解AQS,另一方面大家可以从中了解,如何利用AQS实现自定义Lock。而这儿,对后续JUC下的三大Lock工具的理解有非常大的帮助。 JUC三大工具:经过前两个部分的学习,这个部分不要太easy。可以很容易地理解CountDownLatch,Semaphore,CyclicBarrier的内部运行及实现原理。 不过,由于这三块内容较多,所以我将它拆分为三篇子文章进行论述。 一,介绍 Lock Lock接口位于J.U.C下locks包内,其定义了Lock应该具备的方法。 Lock 方法签名: void lock():获取锁(不死不休,拿不到就一直等)

线程同步(互斥锁与信号量的作用与区别)

主宰稳场 提交于 2020-04-21 16:21:10
摘自: https://www.cnblogs.com/alinh/p/6905221.html “信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里)。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这 个资源。比如对全局变量的访问,有时要加锁,操作完了,在解锁。有的时候锁和信号量会同时使用的” 也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后再进行自己下面的步骤,这个任务 并不一定是锁定某一资源,还可以是进行一些计算或者数据处理之类。而线程互斥量则是“锁住某一资源”的概念,在锁定期间内,其他线程无法对被保护的数据进 行操作。在有些情况下两者可以互换。 两者之间的区别: 作用域 信号量: 进程间或线程间(linux仅线程间的无名信号量pthread semaphore) 互斥锁: 线程间 上锁时 信号量: 只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减一。若value值不大于0,则sem_wait使得线程阻塞,直到sem_post释放后value值加一,但是sem

java nio消息半包、粘包解决方案

寵の児 提交于 2020-04-21 04:42:43
java nio消息半包、粘包解决方案 问题背景 NIO是面向缓冲区进行通信的,不是面向流的。我们都知道,既然是缓冲区,那它一定存在一个固定大小。这样一来通常会遇到两个问题: 消息粘包:当缓冲区足够大,由于网络不稳定种种原因,可能会有多条消息从通道读入缓冲区,此时如果无法分清数据包之间的界限,就会导致粘包问题; 消息不完整:若消息没有接收完,缓冲区就被填满了,会导致从缓冲区取出的消息不完整,即半包的现象。 介绍这个问题之前,务必要提一下我代码整体架构。 代码参见GitHub仓库 https://github.com/CuriousLei/smyl-im 在这个项目中,我的NIO核心库设计思路流程图如下所示 介绍: 服务端为每一个连接上的客户端建立一个Connector对象,为其提供IO服务; ioArgs对象内部实例域引用了缓冲区buffer,作为直接与channel进行数据交互的缓冲区; 两个线程池,分别操控ioArgs进行读和写操作; connector与ioArgs关系:(1)输入,线程池处理读事件,数据写入ioArgs,并回调给connector;(2)输出,connector将数据写入ioArgs,将ioArgs传入Runnable对象,供线程池处理; 两个selector线程,分别监听channel的读和写事件。事件就绪,则触发线程池工作。 思路 光这样实现

谈谈C++的volatile关键字以及常见的误解

五迷三道 提交于 2020-04-20 19:09:03
转载请保留以下声明   作者: 赵宗晟   出处: https://www.cnblogs.com/zhao-zongsheng/p/9092520.html 近期看到C++标准中对volatile关键字的定义,发现和java的volatile关键字完全不一样,C++的volatile对并发编程基本没有帮助。网上也看到很多关于volatile的误解,于是决定写这篇文章详细解释一下volatile的作用到底是什么。 编译器对代码的优化 在讲volatile关键字之前,先讲一下编译器的优化。 int main() { int i = 0 ; i ++ ; cout << " hello world " << endl; } 按照代码,这个程序会在内存中预留int大小的空间,初始化这段内存为0,然后这段内存中的数据加1,最后输出“hello world”到标准输出中。但是根据这段代码编译出来的程序(加-O2选项),不会预留int大小的内存空间,更不会对内存中的数字加1。他只会输出“hello world”到标准输出中。 其实不难理解,这个是编译器为了优化代码,修改了程序的逻辑。实际上C++标准是允许写出来的代码和实际生成的程序不一致的。虽说优化代码是件好事情,但是也不能让编译器任意修改程序逻辑,不然的话我们没办法写可靠的程序了。所以C++对这种逻辑的改写是有限制的

C++智能指针

房东的猫 提交于 2020-04-20 09:24:06
C++智能指针 来源 https://zhuanlan.zhihu.com/p/30933682 参考 https://www.zhihu.com/question/319277442/answer/1094961099 ======================== 智能指针只能代替 T* 的一部分功能,而这部分本来就不适合用 T* (因为容易造成bug)。 如果本身就没有所有权,Bjarne( P1408 )的建议是直接用 T* 。 ======================== 智能指针表示的是某个资源的“ 所有权 ”的概念, unique_ptr表示唯一的所有权; shared_ptr表示可共享的所有权; weak_ptr表示可共享的未来的所有权。 然而资源拥有者如果只是要把一个对象借给别人用一下,用完就归还呢? void borrow_sth(??? resource); 有两种选择: T* 和 const std::unique_ptr<T>& 我更喜欢用第一种 ======================== 嗯,大家都在说普通指针和智能指针,少说了一个“引用” 先考虑用引用和值,减少90%的指针,特别是函数之间,基本上没有必要传指针。 再考虑使用unique_ptr,替换类的成员不能用引用的情况下使用指针。 最后考虑使用share_ptr/weak_ptr