java并发

一.java语言特性

1、跨平台/可移植性

这是Java的核心优势。Java在设计时就很注重移植和跨平台性。比如:Java的int永远都是32位。不像C++可能是16,32,可能是根据编译器厂商规定的变化。这样的话程序的移植就会非常麻烦。

2、安全性

Java适合于网络/分布式环境,为了达到这个目标,在安全性方面投入了很大的精力,使Java可以很容易构建防病毒,防篡改的系统。

3、面向对象

面向对象是一种程序设计技术,非常适合大型软件的设计和开发。由于C++为了照顾大量C语言使用者而兼容了C,使得自身仅仅成为了带类的C语言,多少影响了其面向对象的彻底性!Java则是完全的面向对象语言。

4、简单性

Java就是C++语法的简化版,我们也可以将Java称之为“C++-”。跟我念“C加加减”,指的就是将C++的一些内容去掉;比如:头文件,指针运算,结构,联合,操作符重载,虚基类等等。同时,由于语法基于C语言,因此学习起来完全不费力。

5、高性能

Java最初发展阶段,总是被人诟病“性能低”;客观上,高级语言运行效率总是低于低级语言的,这个无法避免。Java语言本身发展中通过虚拟机的优化提升了几十倍运行效率。

比如,通过JIT(JUST IN TIME)即时编译技术提高运行效率。 将一些“热点”字节码编译成本地机器码,并将结果缓存起来,在需要的时候重新调用。这样的话,使Java程序的执行效率大大提高,

某些代码甚至接待C++的效率。因此,Java低性能的短腿,已经被完全解决了。业界发展上,我们也看到很多C++应用转到Java开发,很多C++程序员转型为Java程序员。

6、分布式

Java是为Internet的分布式环境设计的,因为它能够处理TCP/IP协议。事实上,通过URL访问一个网络资源和访问本地文件是一样简单的。Java还支持远程方法调用(RMI,Remote Method Invocation),

使程序能够通过网络调用方法。

7、多线程

多线程的使用可以带来更好的交互响应和实时行为。 Java多线程的简单性是Java成为主流服务器端开发语言的主要原因之一。

8、健壮性

Java是一种健壮的语言,吸收了C/C++ 语言的优点,但去掉了其影响程序健壮性的部分(如:指针、内存的申请与释放等)。Java程序不可能造成计算机崩溃。即使Java程序也可能有错误。

如果出现某种出乎意料之事,程序也不会崩溃,而是把该异常抛出,再通过异常处理机制加以处理。

二.并发编程

1.计算机基础知识

1.1:cpu多级缓存

主存和cpu通过主线连接,CPU缓存在主存和CPU之间,缓存的出现可以减少CPU读取共享主存的次数;

为什么需要CPU cache:CPU的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU常常需要等待主存,浪费资源。所以cache的出现,是为了缓解CPU和内存之间速度不匹配问题(结构:cpu -> cache -> memery)。

1.2:.CPU多级缓存-缓存一致性(MESI)

MESI分别代表cache数据的四种状态,这四种状态可以相互转换。

缓存四种操作:local read、local write、remote read、remote write

2.进程与线程

2.1:进程:执行中的程序,一个进程至少包含一个线程

2.2:线程:进程中负责程序执行的执行单元

线程本身依靠程序进行运行,线程是程序中的顺序控制流,只能使用分配给程序的资源和环境

2.3:线程与进程的区别:.一个进程是一个独立(self contained)的运行环境,它可以被看作一个程序或者一个应用。而线程是在进程中执行的一个任务。线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据

3.JMM内存模型(Java Memory Model)

3.1:同步八种操作

3.1.1.lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态

3.1.2.unlock(解锁):作用于主内存变脸个,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定

3.1.3.read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用

3.1.4.write(写入):作用于主内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中

3.1.5.load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中

3.1.6.use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎

3.1.7.assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量

3.1.8.store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以遍随后的write的操作

3.2.同步规则

3.2.1.如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量之前需要重新执行load或assign操作初始化变量的值

3.2.2.一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现

3.2.3.如果一个变量实现没有被lock操作锁定,怎不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定的变量

3.2.4.对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)

3.2.5.不允许read和load、store和write操作之一单独出现

3.2.6.如果要把一个变量从主内存中复制到工作内存,就需要按顺序的执行read和load操作,如果把变量从工作内存中同步回主内存,就需要按顺序的执行store和write操作。但java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行

3.2.7.不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中

3.2.8.不允许一个线程无原因的(没发生过任何assign操作)把数据从工作内存同步回主内存中

3.2.9.一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作

4.什么是线程安全

4.1概述:多线程情况下出现实际结果与预期结果不一致或实际结果不稳定的情况

4.2.出现线程安全问题的前提:

a.多线程

b.共享变量,不止成员变量,其他线程可以访问到的变量也是属于共享的变量(局部变量也可能成为共享变量)

4.3.避免出现线程安全的方法

a.设置共享的不可变量(final修饰的)

b.设置不可共享的变量

c.设置为单线程

d.利用互斥或同步的手段

(同步除了具有互斥的能力还要求顺序性)

5.Java实现同步(或互斥)的三种方式

5.1.synchronized关键字

5.2.Lock接口

5.3.LockSupport类

6.线程六大状态

6.1.NEW

至今尚未启动的线程处于这种状态。

6.2.RUNNABLE

正在 Java 虚拟机中执行的线程处于这种状态。

6.3.BLOCKED

受阻塞并等待某个监视器锁的线程处于这种状态。

6.4.WAITING

无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。

6.5.TIMED_WAITING

等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。

6.6.TERMINATED

已退出的线程处于这种状态。

7.线程池

7.1.线程池七大参数

7.1.1.核心线程数

7.1.2.最大线程数

7.1.3.空闲线程等待时间

7.1.4.时间单位

7.1.5.阻塞队列

7.1.6.线程工厂

7.1.7.拒绝策略

四种拒绝策略

1.AbortPolicy (默认)

用于被拒绝任务的处理程序,它将抛出 RejectedExecutionException.

2.CallerRunsPolicy

用于被拒绝任务的处理程序,它直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务。

3.DiscardOldestPolicy

用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试 execute;如果执行程序已关闭,则会丢弃该任务。

4.DiscardPolicy

用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。

7.2.线程池五大状态

7.2.1.RUNNING(状态值:-1)

7.2.2.SHUTDOWN(状态值:0)

7.2.3.STOP(状态值:1)

7.2.4.TIDYING(状态值:2)

7.2.5.TERMINATED(状态值:3)

7.3.线程池运行过程

8.AQS(抽象队列同步器)

AbstractQueuedSynchronizer

AQS内部的阻塞队列实现原理:基于双向链表,通过对head/tail进行CAS操作,实现入队和出队。

8.1.内部类:Node

将每一个在等待状态中的线程封装在每一个node节点中,并形成先进先出队列结构(FIFO)

8.2.内部类:ConditionObject

实现精准唤醒

8.3.tryAcquire方法由其子类实现,独占模式

8.4.tryRelease方法由其子类实现,独占模式

8.5.tryAcquireShared方法由其子类实现,共享模式

8.6.tryReleaseShared方法由其子类实现,共享模式

9.JUC并发工具包

9.1.Atomic原子类

(利用volatile与CAS原理实现)

9.1.1.AtomicInteger

9.1.2.AtomicLong

9.1.3.AtomicReference

9.1.4.AtomicBoolean

9.1.5.AtomicStampedReference和9.1.6.AtomicMarkableReference

9.1.6.AtomicIntegerFieldUpdater、AtomicLongFieldUpdater和AtomicReferenceFieldUpdater

9.1.7.AtomicIntegerArray、AtomicLongArray和AtomicReferenceArray

9.1.8.Striped64与LongAdder

伪共享与缓存行填充

基本的原子类型,其他原子类型都是

在此基础上进行扩展的

主要解决ABA问题

1.成员变量必须是volatile修饰

2.成员变量必须是int或long的基本类型

(refernce则不能是基本类型否则报错)

9.2.Lock接口

(主要基于AQS队列原理实现)

9.2.1.ReentrantLock

Condition

读与读互斥,读与写互斥,写与写互斥

9.2.2.ReentrantReadWriteLock

读与读不互斥,读与写互斥,写与写互斥

9.2.3.StampedLock

读与读不互斥,读与写不互斥,写与写互斥

9.2.4.LockSupport

9.3.同步工具类

9.3.1.Semaphore

9.3.2.CountDownLatch

9.3.3.CyclicBarrier

9.3.4.Exchanger

9.3.5.Phaser

9.4.并发容器

9.4.1.BlockingQueue

9.4.1.1.ArrayBlockingQueue

ArrayBlockingQueue是一个用数组实现的环形队列,在构造方法中,会要求传入数组的容量。

9.4.1.2.LinkedBlockingQueue

LinkedBlockingQueue是一种基于单向链表的阻塞队列。因为队头和队尾是2个指针分开操作的,

所以用了2把锁+2个条件,同时有1个AtomicInteger的原子变量记录count数。

9.4.1.3.PriorityBlockingQueue

队列通常是先进先出的,而PriorityQueue是按照元素的优先级从小到大出队列的。正因为如此,

PriorityQueue中的2个元素之间需要可以比较大小,并实现Comparable接口。

9.4.1.4.DelayQueue

DelayQueue即延迟队列,也就是一个按延迟时间从小到大出队的PriorityQueue。所谓延迟时间,就是“未来将要执行的时间”减去“当前时间”。为此,放入DelayQueue中的元素,必须实现Delayed接口

9.4.1.5.SynchronousQueue

SynchronousQueue是一种特殊的BlockingQueue,它本身没有容量。先调put(...),线程会阻塞;

直到另外一个线程调用了take(),两个线程才同时解锁,反之亦然。对于多个线程而言,例如3个线程,

调用3次put(...),3个线程都会阻塞;直到另外的线程调用3次take(),6个线程才同时解锁,反之亦然。

9.4.2.BlockingDeque

该接口只有一个实现,就是LinkedBlockingDeque。LinkedBlockingDeque是双向链表。

9.4.3.CopyOnWrite

9.4.3.1.CopyOnWriteArrayList的核心数据结构也是一个数组

9.4.3.2.CopyOnWriteArraySet 就是用 Array 实现的一个 Set,保证所有元素都不重复。其内部是封装的一个CopyOnWriteArrayList。

CopyOnWrite指在“写”的时候,不是直接“写”源数据,而是把数据拷贝一份进行修改,再通过悲观锁或者乐观锁的方式写回。这是为了在“读”的时候不加锁。

9.4.4.ConcurrentLinkedQueue/Deque

1.ConcurrentLinkedQueue它是一个单向链表,它的实现原理和AQS 内部的阻塞队列类似:同样是基于 CAS,同样是通过head/tail指针记录队列头部和尾部,但还是有稍许差别。

2.在ConcurrentLinkedQueue中,head/tail的更新可能落后于节点的入队和出队,因为它不是直

接对 head/tail指针进行 CAS操作的,而是对 Node中的 item进行操作。

9.4.5.ConcurrentHashMap

HashMap通常的实现方式是“数组+链表”,这种方式被称为“拉链法”。ConcurrentHashMap在这个

基本原理之上进行了各种优化。

9.4.6.ConcurrentSkipListMap/Set

9.4.6.1.ConcurrentSkipListMap则是 key 有序的,

实现了NavigableMap接口,此接口又继承了SortedMap接口。

9.4.6.2.是ConcurrentSkipListMap,是基于

SkipList(跳查表)来实现的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值