多线程的艺术
多线程的艺术
软件求生
这个作者很懒,什么都没留下…
展开
-
并发:缓存一致性(硬件层面)。
并发:缓存一致性(硬件层面)。转载 2020-04-10 09:50:02 · 334 阅读 · 0 评论 -
多线程:无锁。
无锁即无障碍的运行, 所有线程都可以到达临界区, 接近于无等待.无锁采用CAS(compare and swap)算法来处理线程冲突, 其原理如下CAS原理CAS包含3个参数CAS(V,E,N).V表示要更新的变量, E表示预期值, N表示新值.仅当V值等于E值时, 才会将V的值设为N, 如果V值和E值不同, 则说明已经有其他线程做了更新, 则当前线程什么都不做.。最后, CAS返...转载 2019-11-29 15:28:35 · 935 阅读 · 0 评论 -
并发:Actor模型。
前言一般来说有两种策略用来在并发线程中进行通信:共享数据和消息传递。熟悉c和java并发编程的都会比较熟悉共享数据的策略,比如java程序员就会常用到java.util.concurrent包中同步、锁相关的数据结构。使用共享数据方式的并发编程面临的最大的一个问题就是数据条件竞争(data race)。处理各种锁的问题是让人十分头痛的一件事。和共享数据方式相比,消息传递机制最大的优点...转载 2019-11-29 10:31:19 · 481 阅读 · 0 评论 -
多线程:线程安全的实现方法。
互斥同步互斥同步(Mutual Exclusion&Synchronization)是常见的一种并发正确性保障手段。同步是指多个线程并发访问数据时,保证共享数据在同一个时刻只被一个线程(或者是一些,使用信号量的时候)使用。而互斥是实现同步的一种手段,临界区(Critical Section)、互斥量(Mutex)和信号量(Semaphore)都是主要的互斥实现方式。因此,在这4个字里面...转载 2019-06-06 17:17:08 · 228 阅读 · 0 评论 -
多线程:线程安全。
线程安全“线程安全”这个名称,相信稍有经验的程序员都会听说过,甚至在代码编写和走查的时候可能还会经常挂在嘴边,但是如何找到一个不太拗口的概念来定义线程安全却不是一件容易的事情,这里尝试在Google中搜索他的概念,找到的是类似于“如果一个对象可以安全的被多个线程同时使用,那他就是线程安全的”这样的定义——并不能说他不正确,但是人们无法从中获取到任何有用的信息。这里认为《Java Concur...转载 2019-06-06 15:16:25 · 152 阅读 · 0 评论 -
多线程:如何合理地估算线程池大小?
如何合理地估算线程池大小转载 2019-11-19 17:16:34 · 409 阅读 · 0 评论 -
并发:ScheduledThreadPoolExecutor详解。
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。他主要用来给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor的功能与Timer类似,但ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而ScheduledThreadPoolExecutor可以在构...转载 2018-12-12 16:25:06 · 1676 阅读 · 2 评论 -
并发:FutureTask详解。
FutureTask接口和实现Future接口的FutureTask类,代表异步计算的结果。FutureTask简介FutureTask除了实现Future接口外,还实现了Runnable接口。因此,FutureTask可以交给Executor执行也可以由当调用线程直接执行(FutureTask.run())。根据FutureTask.run()方法被执行的时机,FutureTask可以处...转载 2018-12-13 09:23:03 · 1037 阅读 · 0 评论 -
并发:读写锁(ReentrantReadWriteLock)。
ReentrantLock之类的基本都是排它锁,这些锁在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排它锁有了很大提升。除了保证写操作对读操作的可见性以及并发行的提升之外,读写锁能够简化读写交互场景的编程方式。假设在程序中定义一个共...转载 2018-12-06 11:29:04 · 1110 阅读 · 0 评论 -
并发:LockSupport工具。
当需要阻塞或唤醒一个线程的时候,都会使用LockSupport工具类来完成相应工作。LockSupport定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能,而LockSupport也成为构建同步组件的基础工具。LockSupport定义了一组以park开头的方法用来阻塞当前线程,以及uppark(Thread thread)方法来唤醒一个被阻塞的线程。Park有停车的意思,假...转载 2018-12-06 13:50:42 · 278 阅读 · 0 评论 -
并发:Condition接口。
任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait()、wait(long timeout)、notify()以及notifyAll()方法,这些方法与synchronized同步关键字配合,可以实现等待/通知模式。Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性...转载 2018-12-06 16:09:48 · 207 阅读 · 0 评论 -
并发:重入锁(ReentrantLock)。
重入锁,顾名思义,就是支持冲进入的锁,他表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁的还支持获取锁时的公平和非公平性选择。synchronized关键字隐式的支持重进入,比如一个synchronized修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次的获取该锁。ReentrantLock虽然没能像synchronized关键字一样支持隐式的重进入,但是在调用lo...转载 2018-12-05 17:20:42 · 433 阅读 · 0 评论 -
并发:ThreadPoolExecutor详解。
Executor框架最核心的类是ThreadPoolExecutor,他是线程池的实现类,主要由下列4个组件构成。corePool:核心线程池的大小。 maximumPool:最大线程池的大小。 BlockingQueue:用来暂时保存任务的工作队列。 RegjectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor...转载 2018-12-12 15:45:36 · 585 阅读 · 0 评论 -
并发:Executor框架简介。
在HotSpot VM的线程模型中,Java线程(java.lang.Thread)被一对一映射为本地操作系统线程。Java线程启动时会创建一个本地操作系统线程;当该Java线程终止时,这个操作系统线程也会被回收。操作系统会调度所有线程并将他们分配给可用的CPU。在上层,java多线程程序通常把应用分解为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;...转载 2018-12-12 14:56:25 · 314 阅读 · 0 评论 -
并发:双重检查锁定(Double-Checked Locking)与延迟初始化(Lazy Initialization)。
在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销。双重检查锁定是常见的延迟初始化技术,但他是一个错误的用法。本文将分析双重检查锁定的错误根源,以及两种线程安全的延迟初始化方案。双重检查锁定的由来在Java程序中,有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化。此时程序员可能会采用延迟初始化。但要正确实现线程安全的延迟初始化需...转载 2018-12-03 15:04:49 · 1340 阅读 · 1 评论 -
并发:队列同步器(AbstractQueuedSynchronizer)。
队列同步器(以下简称同步器),是用来构建锁和或者其他同步组件的基础框架,他使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,并发包的作者期望他能够成为实现大部分同步需求的基础。同步器的主要使用方式是继承,子类通过继承同步器并实现他的抽象方法来管理同步状态,在抽象方法的实现过程中免不了要对同步状态进行更改,这时就需要使用同步器提供的3个方法(getStat...转载 2018-12-05 15:49:19 · 312 阅读 · 0 评论 -
并发:happens-before。
happens-before是JMM最核心的概念。对应Java程序员来说,理解happens-before是理解JMM的关键。JMM的设计首先,让我们来看JMM的设计意图。从JMM设计者的角度,在设计JMM时,需要考虑两个关键因素。程序员对内存模型的使用。程序员希望内存模型易于理解、易于编程。程序员希望基于一个强内存模型来编写代码。 编译器和处理器对内存模型的实现。编译器和处理器希望...转载 2018-11-27 11:51:14 · 163 阅读 · 0 评论 -
并发:final域的内存语义。
与锁和volatile相比,对final域的读和写更像是普通的变量访问。下面将介绍final域的内存语义。final域的重排序规则对于final域,编译器和处理器要遵守两个重排序规则。在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能...转载 2018-11-27 10:38:07 · 336 阅读 · 0 评论 -
并发:ConcurrentLinkedQueue(非阻塞的线程安全队列)。
在并发编程中,有时候需要使用线程安全的队列。如果要实现一个线程安全的队列有两种方式:一种是使用阻塞算法,另一种是使用非阻塞算法。使用阻塞算法的队列可以用一个锁(入队和出队用同一把锁)或两个锁(入队和出队用不同的锁)等方式来实现。非阻塞的实现方式则可以使用循环CAS的方式来实现。ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,他采用先进先出的规则对节点进行排序,当...转载 2018-12-10 15:07:53 · 1559 阅读 · 0 评论 -
并发:Java中的阻塞队列(BlockingQueue)。
本文将介绍什么是阻塞队列,以及Java中阻塞队列的4种处理方式,并介绍Java 7中提供的7种阻塞队列,最后分析阻塞队列的一种实现方式。什么是阻塞队列阻塞队列是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。 支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空。...转载 2018-12-11 09:03:54 · 826 阅读 · 0 评论 -
java:线程。
并发不一定要依赖多线程(如PHP中很常见的多进程并发),但是在Java里面谈论并发,大多数都与线程脱不开关系。线程的实现我们知道,线程是比进程更轻量级的调度执行单位,线程的引入,可以把一个进程的资源分配和执行调度分开,各个线程既可以共享进程资源,又可以独立调度(线程是CPU调度的基本单位)。主流的操作系统都提供了线程实现,Java语言则提供了在不同硬件和操作系统平台下对线程操作的统一处理...转载 2019-06-11 13:26:52 · 598 阅读 · 0 评论 -
并发:硬件的效率与一致性。
“让计算机并发执行若干个运算任务”与“更充分的利用计算机处理器的效能”之间的因果关系,看起来顺理成章,实际上他们之间的关系并没有想象中那么简单,其中一个重要的复杂性来源是绝大多数的运算任务都不可能只靠处理器“计算”就能完成,处理器至少要与内存交互,如读取运算数据、存储运算结果等,这个I/O操作是很难消除的(无法仅靠寄存器来完成所有运算任务)。由于计算机的存储设备与处理器的运算速度有几个数量级的差距...转载 2019-06-10 13:33:33 · 607 阅读 · 0 评论 -
并发:锁优化。
高效并发是从JDK1.5到JDK1.6的一个重要改进,HotSpot虚拟机开发团队在这个版本上花费了大量的精力去实现各种锁优化技术,如适应性自旋(Adaptive Spinning)、锁销除(Lock Elimination)、锁粗化(Lock Coarsening)、轻量级锁(Lightweight Locking)和偏向锁(Biased Locking)等,这些技术都是为了在线程之间更高效的共...转载 2019-05-16 14:55:50 · 124 阅读 · 0 评论 -
Java并发编程:Lock。
synchronized的缺陷 synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢? 如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:获取锁的线程执行完了该代码块,然后线程释放对锁的占有; ...转载 2019-04-02 15:23:04 · 148 阅读 · 0 评论 -
Java:线程池的使用。
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。 那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务? 在Java中可以通过线程池来达到这样的效果。今天我们就来详细讲解一下Java的线程池,首先我们从最核心的ThreadPoolExecutor类中的方法讲起,然后再讲述它的实现原理,接着给出了它的使用示例,最后讨论了一下如何合理配置线程池的大小。转载 2019-04-01 10:09:52 · 112 阅读 · 0 评论 -
并发:线程池的使用。
线程池的创建我们可以通过ThreadPoolExecutor来创建一个线程池。new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds, runnableTaskQueue, handler);创建一个线程池时需要输入几个参数。如下。corePoolSize(线程池的基本大小...转载 2018-12-12 10:42:59 · 160 阅读 · 0 评论 -
并发:CyclicBarrier和CountDownLatch的区别。
CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重置。所以CyclicBarrier能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数器,并让线程重新执行一次。CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获取CyclicBarrier阻塞的线程数量。isBroken()方...转载 2018-12-11 15:02:11 · 278 阅读 · 0 评论 -
并发:CyclicBarrier(同步屏障)。
CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。他要做的事情是,让一组线程达到一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程达到屏障时,屏障才会开门,所有屏障拦截的线程才会继续运行。CyclicBarrier简介CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线...转载 2018-12-11 14:55:05 · 775 阅读 · 0 评论 -
并发:等待多线程完成的CountDownLatch(倒数计时器)。
CountDownLatch允许一个或多个线程等待其他线程完成操作。假如有这样一个需求:我们需要解析一个Excel里多个sheet的数据,此时可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。在这个需求中,要实现主线程等待所有线程完成sheet的解析操作,最简单的做法是使用join()方法,如下所示。public class ...转载 2018-12-11 14:12:13 · 555 阅读 · 0 评论 -
并发:Java中的13个原子操作类。
当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可能i不等于3,而是等于2。因为A和B线程在更新变量i的时候拿到的i都是1,这就是线程不安全的更新操作,通常我们会使用synchronized来解决这个问题,synchronized会保证多线程不会同时更新变量i。而Java从JDK 1.5开始提供了...转载 2018-12-11 13:36:03 · 228 阅读 · 0 评论 -
并发:线程间交换数据的Exchanger(交换者)。
Exchanger是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。他提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据,如果第一个线程先执行exchange()方法,他会一直等待第二个线程也执行exchange方法,当两个线程都达到同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。下面来看一下Exc...转载 2018-12-11 15:50:46 · 412 阅读 · 0 评论 -
并发:控制并发线程数的Semaphore(信号量)。
Semaphore是用来控制同时访问特定资源的线程数量,他通过协调各个线程,以保证合理的使用公平资源。多年以来,我都觉得从字面上很难理解Semaphore所表达的含义,只能把他比作是控制流量的红绿灯。比如xx马路要限制流量,只允许同时有一百两车在这条路上行使,其他的都必须在路口等待,所以前一百辆车会看到绿灯,可以开进这条马路,后面的车会看到红灯,不能驶入xx马路,但是如果前一百辆中有5辆车已经...转载 2018-12-11 15:42:18 · 974 阅读 · 0 评论 -
并发:锁的内存语义。
众所周知,锁可以让临界区互斥执行。这里将介绍锁的另一个同样重要,但常常被忽视的功能:锁的内存语义。锁的释放——获取建立的happens-before关系锁是Java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。下面是锁释放——获取的示例代码。class MonitorExample { int a = 0; ...转载 2018-11-26 17:21:11 · 441 阅读 · 0 评论 -
并发:volatile的内存语义。
当声明共享变量为volatile后,对这个变量的读/写将会很特别。为了揭开volatile的神秘面纱,下面将介绍volatile的内存语义及volatile内存语义的实现。volatile的特性理解volatile特性的一个好方法是把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。下面通过具体的示例来说明,示例代码如下。class Volatile...转载 2018-11-26 15:28:16 · 235 阅读 · 0 评论 -
java锁优化的方法与思路。
java程序开发中一旦用到锁,就表示采用了阻塞形式的并发——一种最糟糕的并发等级。而锁优化就是希望在高并发多线程程序当中将涉及到有锁动作的相关代码尽可能的加以改进,使执行效率尽可能地得到提升。当然就算将这种用到了锁的代码优化到极致,其性能也无法超越无锁,毕竟锁会导致线程挂起(相对来说相当耗时及浪费资源)。但是我们要想办法让这种损耗降到最低,这是锁优化的出发点。 一般来说,java锁优化有如下思路或...转载 2018-06-29 16:10:29 · 349 阅读 · 3 评论 -
Future模式。
先上一个场景:假如你突然想做饭,但是没有厨具,也没有食材。网上购买厨具比较方便,食材去超市买更放心。实现分析:在快递员送厨具的期间,我们肯定不会闲着,可以去超市买食材。所以,在主线程里面另起一个子线程去网购厨具。但是,子线程执行的结果是要返回厨具的,而run方法是没有返回值的。所以,这才是难点,需要好好考虑一下。模拟代码1:package test;public class CommonCoo...转载 2018-06-28 10:25:30 · 183 阅读 · 0 评论 -
并发:资源限制的挑战。
什么是资源限制资源限制是指在进行并发编程时,程序的执行速度受限于计算机硬件资源或软件资源。例如,服务器的带宽只有2Mb/s,某个资源的下载速度是1Mb/s每秒,系统启动10个线程下载资源,下载速度不会变成10Mb/s,所以在进行并发编程时,要考虑这些资源的限制。硬件资源限制有带宽的上传/下载速度、硬盘读写速度和CPU的处理速度。软件资源限制有数据库的连接数和socket连接数等。资源限制引...转载 2018-11-20 11:14:13 · 281 阅读 · 0 评论 -
解决方案:避免死锁的几个常见方法。
避免一个线程同时获取多个锁。 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。建议多使用JDK并发包提供的并发容器和工具类来解决并发问题,因为这些类都已经通过了充分的测试和优化。...转载 2018-11-20 10:31:31 · 1941 阅读 · 0 评论 -
并发:上下文切换。
即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。时间片是CPU分配给各个线程的时间,因为时间片非常短,所以CPU通过不停的切换线程执行,让我们感觉多个线程是同时执行的,时间片一般是几十毫秒(ms)。CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是,在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加...转载 2018-11-20 10:26:24 · 305 阅读 · 1 评论 -
并发:重排序。
重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。数据依赖性如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分为下列3种类型,如下表所示。名称 代码示例 说明 写后读 a=1; b=a; 写一个变量之后,再读这个位置 写后写 a=1;...转载 2018-11-22 19:39:21 · 328 阅读 · 0 评论