本系列博客笔记源于炼数成金教程而写的笔记
韶关ssl适用于网站、小程序/APP、API接口等需要进行数据传输应用场景,ssl证书未来市场广阔!成为创新互联的ssl证书销售渠道,可以享受市场价格4-6折优惠!如果有意向欢迎电话联系或者加微信:028-86922220(备注:SSL证书合作)期待与您的合作!
开始前必须清楚知道以下几点:
一、关于高并发的几个重要概念
1.1 线程是什么:是操作系统能够进行运算调度的最小单位,在Java中线程是提供给开发者的实现并发机制的一种有效手段,而多个线程可以产生于一个进程。即线程是进程的一个单位
1.2 进程是什么:进程是系统在执行某个程序关于数据集合上的一次活动,是计算机在进行资源分配和调度的基本单位,是操作系统的基本执行实体。
1.3 同步、异步概念:同步、异步即是方法调用时线程或者是进程在执行时的方式,那么有什么不同之处?同步:调用方法时等待方法的返回。异步:调用方法会即刻返回,但是这里返回并不是指方法执行任务已经完成,异步的机制就是调用的同时在后台另起线程继续执行任务。
1.4 并行与并发:
并发与并行在外在表象来看是一样的。但是实际执行的步骤并非一样,并行是指两个任务同时进行,而并发是指两个任务,一会儿执行A任务,一会儿执行B任务。ps: 所以这里如果是单核(也就是单个CPU)是不能做并行任务的,只能做并发任务。
1.5 临界区:临界区用来表示一种公共的资源(或者说是公共的数据),可以被多个线程使用,但是每一次只能有一个线程使用它,一旦这个临界区被占用,其他线程将无法使用这个资源,要想使用就必须等待。
1.6 阻塞与非阻塞
阻塞和非阻塞通常形容多线程之间的相互影响。比如一个线程占用了临界区资源,那么其他线程要想得到这个资源就必须在这个临界区中进行等待,等待会导致线程挂起,这种情况就是阻塞。同时如果占用这个资源的线程一直不愿意释放这个资源,那么其他阻塞在这个临界区上的线程将都不能工作。
非阻塞是允许多个线程同时进入这个临界区,但是这样一来并不能保证共享数据的安全。
1.7 死锁、饥饿、活锁
死锁:通常是指两个或者两个以上的进程在执行过程中,由于竞争资源或者由于彼此通讯而造成的一种阻塞现象,若此间无外力干涉,它们将都无法继续执行下去,此时称系统处于死锁状态或系统产生死锁。
虽然死锁是不好的现象,但是它是一个静态的问题,一旦发生死锁,进程被卡,cpu的占有率也是0,它并不会占用住CPU,它会被调用出去。相对于活锁来说比较好发现和分析。
活锁:是指事务A可以使用资源,但是它在检测到其他事务也将执行时会让其他事务先使用此资源,事务B可以使用此资源,但是它也检测到其他事务将执行所以也让其他事务先使用此资源,于是两者都处于谦让状态,都无法使用此资源。
(举个例子,就如同你在街上遇到个人,刚好他朝着你的反方向走,与你正面碰到,你们都想让彼此过去。你往左边移,他也往左边移,两人还是无法过去。这时你往右边移,他也往右边移,如此循环下去。
一个线程在取得了一个资源时,发现其他线程也想到这个资源,因为没有得到所有的资源,为了避免死锁把自己持有的资源都放弃掉。如果另外一个线程也做了同样的事情,他们需要相同的资源,比如A持有a资源,B持有b资源,放弃了资源以后,A又获得了b资源,B又获得了a资源,如此反复,则发生了活锁。
活锁会比死锁更难发现,因为活锁是一个动态的过程。)ps:原文抄自炼数成金!!!
饥饿:是指某一个或者多个线程因为种种原因无法获取得到所需要的资源,导致一直无法执行。
1.8 并发级别:阻塞、非阻塞(分为无障碍、无锁、无等待)
1.8.1 阻塞:上文讲到当一个线程进入临界区后,其他线程必须等待,此状态称为阻塞
1.8.2 无障碍
无障碍时一种最弱的非阻塞调度
自由出入临界区
无竞争时,有限步内完成操作
有竞争时,回滚数据
和非阻塞调度相比呢,阻塞调度是一种悲观的策略,它会认为说一起修改数据是很有可能把数据改坏的。而非阻塞调度呢,是一种乐观的策略,它认为大家修改数据未必把数据改坏。但是它是一种宽进严出的策略,当它发现一个进程在临界区内发生了数据竞争,产生了冲突,那么无障碍的调度方式则会回滚这条数据。(上文提到即共享数据不安全)
在这个无障碍的调度方式当中,所有的线程都相当于在拿去一个系统当前的一个快照。他们一直会尝试拿去的快照是有效的为止。
1.8.3 无锁
是无障碍的
保证有一个线程可以胜出
与无障碍相比,无障碍并不保证有竞争时一定能完成操作,因为如果它发现每次操作都会产生冲突,那它则会不停地尝试。如果临界区内的线程互相干扰,则会导致所有的线程会卡死在临界区,那么系统性能则会有很大的影响。
同时无锁增加了一个新的条件,保证每次竞争有一个线程可以胜出,则解决了无障碍的问题。至少保证了所有线程都顺利执行下去。
无锁在Java中很常见
while (!atomicVar.compareAndSet(localVar, localVar+1))
{
localVar = atomicVar.get();
}
1.8.4 无等待
无锁的
要求所有的线程都必须在有限步内完成
无饥饿的
首先无等待的前提是无锁的基础上的,无锁它只保证了临界区肯定有进也有出,但是如果进的优先级都很高,那么临界区内的某些优先级低的线程可能发生饥饿,一直出不了临界区。那么无等待解决了这个问题,它保证所有的线程都必须在有限步内完成,自然是无饥饿的。
无等待是并行的最高级别,它能使这个系统达到最优状态。
无等待的典型案例:
如果只有读线程,没有线线程,那么这个则必然是无等待的。
如果既有读线程又有写线程,而每个写线程之前,都把数据拷贝一份副本,然后修改这个副本,而不是修改原始数据,因为修改副本,则没有冲突,那么这个修改的过程也是无等待的。最后需要做同步的只是将写完的数据覆盖原始数据。
由于无等待要求比较高,实现起来比较困难,所以无锁使用得会更加广泛一些。PS:抄自练数成金