CAS

Java并发编程中的若干核心技术,向高手进阶!

谁说胖子不能爱 提交于 2020-05-07 14:48:42
来源:简书 http://www.jianshu.com/p/5f499f8212e7 引言 本文试图从一个更高的视角来总结Java语言中的并发编程内容,希望阅读完本文之后,可以收获一些内容,至少应该知道在Java中做并发编程实践的时候应该注意什么,应该关注什么,如何保证线程安全,以及如何选择合适的工具来满足需求。 当然,更深层次的内容就会涉及到JVM层面的知识,包括底层对Java内存的管理,对线程的管理等较为核心的问题,当然,本文的定位在于抽象与总结,更为具体而深入的内容就需要自己去实践,考虑到可能篇幅过长、重复描述某些内容,以及自身技术深度等原因,本文将在深度和广度上做一些权衡,某些内容会做一些深入的分析,而有些内容会一带而过,点到为止。 总之,本文就当是对学习Java并发编程内容的一个总结,以及给那些希望快速了解Java并发编程内容的读者抛砖引玉,不足之处还望指正。 Java线程 一般来说,在java中实现高并发是基于多线程编程的,所谓并发,也就是多个线程同时工作,来处理我们的业务,在机器普遍多核心的今天,并发编程的意义极为重大,因为我们有多个cpu供线程使用,如果我们的应用依然只使用单线程模式来工作的话,对极度浪费机器资源的。所以,学习java并发知识的首要问题是:如何创建一个线程,并且让这个线程做一些事情? 这是java并发编程内容的起点,下面将分别介绍多个创建线程

多线程高并发编程(7) -- Future源码分析

霸气de小男生 提交于 2020-05-06 15:34:27
一.概念    A Future计算的结果。 提供方法来检查计算是否完成,等待其完成,并检索计算结果。 结果只能在计算完成后使用方法get进行检索,如有必要,阻塞,直到准备就绪。 取消由cancel方法执行。 提供其他方法来确定任务是否正常完成或被取消。 计算完成后,不能取消计算。 如果您想使用Future ,以便不可撤销,但不提供可用的结果,则可以声明Future<?>表格的类型,并返回null作为基础任务的结果。 public interface Future<V> { // 尝试取消执行此任务。如果任务已经完成,已经被取消或由于某些其他原因而无法取消,则此尝试将失败。 // 如果成功,并且在调用 cancel 时此任务尚未开始,则该任务永远无法运行。 // 如果任务已经开始,则 mayInterruptIfRunning 参数确定是否应中断执行该任务的线程以尝试停止该任务。 // mayInterruptIfRunning == true, 表示中断执行中的线程,false 表示让线程正常完成 boolean cancel( boolean mayInterruptIfRunning); // 如果此任务在正常完成之前被取消,则返回true。 boolean isCancelled(); // 如果此任务完成,则返回true。完成可能是由于正常终止,异常或取消引起的

Java_17:volatile和AtomicInteger

被刻印的时光 ゝ 提交于 2020-05-06 11:06:06
1. volatile 1.什么是volatile volatile是Java虚拟机提供的轻量级的同步机制,保证了可见性和有序性(禁止指令重排序),保证了JMM三个特性中的两个 2.JMM-Java内存模型 JMM的三个特性: 可见性、有序性、原子性 可见性: 线程在自己的工作内存中修改了从主内存中拷贝的共享变量副本后,并把修改后的值重新传到主内存中进行更新。这时我们要保证其他线程第一时间也可以得到共享变量已经被修改的通知。这样就保证了线程之间的一个可见性(因为线程间是不能直接访问对方的工作内存,所以可以从主内存下手) 有序性: 禁止指令重排,避免多线程的环境下,程序出现乱序执行的现象。 指令重排:计算机在执行程序时,为了提高性能,编译器和处理器常常回对指令做重排 原子性: 某个线程正在做某个具体业务时,中间不可以被加塞或者被分割。需要整体完整,要么同时成功,要么同时失败。 2验证volatile的特性 2.1 可见性 package volatileTest; /** * 验证volitile的可见性 * @author zhaomin * @date 2020/4/21 15:44 */ class MyData{ volatile int num=0; public void addTo60(){ this.num=70; } } public class Test1 {

当面试官问你:“如何使用乐观锁优化并发性能”,你该怎么回答?

試著忘記壹切 提交于 2020-05-06 11:00:20
乐观锁,顾名思义,就是说在操作共享资源时,它总是抱着乐观的态度进行,它认为自己可以成功地完成操作。 但实际上,当多个线程同时操作一个共享资源时,只有一个线程会成功,那么失败的线程呢? 乐观锁不会像悲观锁一样在操作系统中挂起,而仅仅是返回,并且系统允许失败的线程重试,也允许自动放弃退出操作。 所以,乐观锁相比悲观锁来说,不会带来死锁、饥饿等活性故障问题,线程间的相互影响也远远比悲观锁要小。更为重要的是,乐观锁没有因竞争造成的系统开销,所以在性能上也是更胜一筹。 乐观锁的实现原理 CAS 是实现乐观锁的核心算法,它包含了 3 个参数:V(需要更新的变量)、E(预期值)和 N(最新值)。 只有当需要更新的变量等于预期值时,需要更新的变量才会被设置为最新值,如果更新值和预期值不同,则说明已经有其它线程更新了需要更新的变量,此时当前线程不做操作,返回 V 的真实值。 CAS 如何实现原子操作 在 JDK 中的 concurrent 包中,atomic 路径下的类都是基于 CAS 实现的。AtomicInteger 就是基于 CAS 实现的一个线程安全的整型类。下面我们通过源码来了解下如何使用 CAS 实现原子操作。 我们可以看到 AtomicInteger 的自增方法 getAndIncrement 是用了 Unsafe 的 getAndAddInt 方法,显然 AtomicInteger

面试题:项目经验 已看1 看不懂

柔情痞子 提交于 2020-05-06 07:12:44
项目经验 面试官在一开始会让你进行自我介绍,主要是想让你介绍一下自己做过的一些项目,看看你对这些项目的了解程度,因为很多人简历上写的项目并非都是从头到尾都参与的,有些只是参与并实现了其中的一些模块而已,或是接手维护别人的项目,所以在你简历上所写的和面试过程中所说的项目经验,你自己必须能够了解来龙去脉,因为面试官肯定会根据你的项目描述,对项目中的实现原理,或为什么要这样实现进行提问,这时不至于木讷住而不知如何作答,如此局面只会大大降低面试分。 场景对话: 面试官:(拿着简历)讲讲你最近做的这个项目 我:&……%¥#*&¥@%¥!,说了一大通(不知道面试官听进去多少,面试官会挑他会的进行提问) 面试官:你说这个项目中用到了netty,能大概讲讲netty的线程模型么? 我:(幸好我看过netty的源码)netty通过Reactor模型基于多路复用器接收并处理用户请求(能讲就多讲一点),内部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责处理请求的accept事件,当接收到accept事件的请求时,把对应的socket封装到一个NioSocketChannel中,并交给work线程池,其中work线程池负责请求的read和write事件(通过口述加画图的方式,把请求的执行过程大概描述了一遍,时间有限,也不可能把所有的细节都说完,挑重点讲,挑记忆深刻的讲)

面试官:你分析过线程池源码吗?

自闭症网瘾萝莉.ら 提交于 2020-05-06 03:36:52
线程池源码也是面试经常被提问到的点,我会将全局源码做一分析,然后告诉你面试考啥,怎么答。 为什么要用线程池? 简洁的答两点就行。 降低系统资源消耗。 提高线程可控性。 如何创建使用线程池? JDK8提供了五种创建线程池的方法: 1.创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 1 public static ExecutorService newFixedThreadPool( int nThreads) { 2 return new ThreadPoolExecutor(nThreads, nThreads, 3 0L , TimeUnit.MILLISECONDS, 4 new LinkedBlockingQueue<Runnable> ()); 5 } 2.(JDK8新增)会根据所需的并发数来动态创建和关闭线程。能够合理的使用CPU进行对任务进行并发操作,所以适合使用在很耗时的任务。 注意返回的是ForkJoinPool对象。 1 public static ExecutorService newWorkStealingPool( int parallelism) { 2 return new ForkJoinPool 3 (parallelism, 4 ForkJoinPool.defaultForkJoinWorkerThreadFactory,

ConCurrentHashMap在1.7和1.8区别

我怕爱的太早我们不能终老 提交于 2020-05-05 18:36:01
ConCurrentHashMap 1.8 相比 1.7的话,主要改变为: 去除 Segment + HashEntry + Unsafe 的实现, 改为 Synchronized + CAS + Node + Unsafe 的实现 其实 Node 和 HashEntry 的内容一样,但是HashEntry是一个内部类。 用 Synchronized + CAS 代替 Segment ,这样锁的粒度更小了,并且不是每次都要加锁了,CAS尝试失败了在加锁。 put()方法中 初始化数组大小时,1.8不用加锁,因为用了个 sizeCtl 变量,将这个变量置为-1,就表明table正在初始化。 下面简单介绍下主要的几个方法的一些区别: 1. put() 方法 JDK1.7中的实现: ConCurrentHashMap 和 HashMap 的put()方法实现基本类似,所以主要讲一下为了实现并发性,ConCurrentHashMap 1.7 有了什么改变 需要定位 2 次 (segments[i],segment中的table[i]) 由于引入segment的概念,所以需要 先通过key的 rehash值的高位 和 segments数组大小-1 相与得到在 segments中的位置 然后在通过 key的rehash值 和 table数组大小-1 相与得到在table中的位置 没获取到

乐观锁常见的两种实现方式和适用场景

爷,独闯天下 提交于 2020-05-05 18:06:50
1、版本号机制 一般是在数据表中加上一个版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读到的version值与当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。 举一个简单的例子: 假设数据库中账户信息表中有一个version字段,当前值为1;而当前账户余额字段为100; 1、操作员A此时将其读出(version=1),并从其账户余额中扣除50(100-50); 2、在操作员A操作的过程中,操作员B也读入此用户信息(version=1),并从其账户余额中扣除20(100-20); 3、操作员A完成了修改操作,将数据版本号+1(version=2),连同账户扣除后余额(子段=50),提交至数据库更新,此时由于 提交数据库版本大于数据库当前记录的版本,数据被更新,数据库记录version更新为2. 4、操作员B完成了操作,也将版本号+1(version=2)试图向数据库提交数据(子段=80),但此时比对数据库记录版本时发现,操作员B提交的数据版本号为2,数据库记录当前版本也为2,不满足“提交版本必须大于记录当前版本才能执行更新”的乐观锁策略,因此,操作员B的提交被驳回。 这样就避免了操作员B用基于version

Redis的消息订阅及发布及事务机制

我怕爱的太早我们不能终老 提交于 2020-05-05 15:54:24
Redis的消息订阅及发布及事务机制 订阅发布 SUBSCRIBE PUBLISH 订阅消息队列及发布消息。 # 首先要打开redis-cli shell窗口 一个用于消息发布 一个用于消息订阅 # SUBSCRIBE 订阅一个频道,如果频道不存在 就新增一个 # 返回参数 表示 第一个是命令 第二个是频道名称 第三个表示当前订阅该频道的数量 127.0.0.1:6379> SUBSCRIBE mychannel Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "mychannel" 3) (integer) 1 # 在另外一个shell窗口 进行消息发布 127.0.0.1:6379> PUBLISH mychannel "hello world" (integer) 1 # 在回到之前的shell 窗口 我们可以看到消息已经被订阅成功了 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "mychannel" 3) (integer) 1 # 下面是本次接受到消息 1) "message" 2) "mychannel" 3) "hello world" UNSUBSCRIBE UNSUBSCRIBE:取消订阅。 #

002-多线程-锁-同步锁-synchronized几种加锁方式、Java对象头和Monitor、Mutex Lock、JDK1.6对synchronized锁的优化实现

两盒软妹~` 提交于 2020-05-05 14:47:59
一、synchronized概述基本使用   为确保共享变量不会出现并发问题,通常会对修改共享变量的代码块用 synchronized 加锁,确保同一时刻只有一个线程在修改共享变量,从而避免并发问题。   synchronized结论:     1、java5.0之前,协调线程间对共享对象的访问的机制只有synchronized和volatile,但是内置锁在功能上存在一些局限性,jdk5增加了Lock以及ReentrantLock。     2、java5.0,增加了一种新的机制:显式锁ReentrantLock,注意它并不是替代内置锁synchronized的机制,而是当内置锁不适用时,作为一种可选的高级功能。     3、jdk6之后,synchronized与java.util.concurrent包中的ReentrantLock相比,由于JDK1.6中加入了针对锁的优化措施,使得synchronized与ReentrantLock的性能基本持平。ReentrantLock只是提供了synchronized更丰富的功能,而不一定有更优的性能,所以在synchronized能实现需求的情况下,优先考虑使用synchronized来进行同步。     synchronized在JDK5之前一直被称为重量级锁,底层是使用操作系统的mutex lock实现的,是一个较为鸡肋的设计