![](https://img-blog.csdnimg.cn/20201014180756922.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
多线程
文章平均质量分 87
大将黄猿
一只业余爱好广泛的程序猿!
展开
-
synchronized的实现原理
锁的内存语义线程释获取锁时JMM会把对应的本地内存置为无效,从而使得被监视器保护的临界区域代码必须从主内存中读取共享变量。也就是说锁内的临界区变量,是绝对不会走CPU缓存的,都是从主内存中重新获取的"新鲜"数据。线程释放锁时JMM会把该线程对应的本地变量内存中的共享变量刷新到主内存中。(但这里不能保证刷新到主内存的数据对其他线程的可见性)案例解释内存语义加voliate解决可见性来看下面这个例子:我们为了让子线程可以及时看到ready变量的修改,我们将read以vol原创 2021-04-06 01:46:31 · 191 阅读 · 0 评论 -
final的内存语义
在构造线程的类时,我们有种方式就是让类中所有的成员变量都不可变。利用的就是final关键字。那么这个final关键字是如何做到的呢?重排序这种优化动作对构造方法一样也是存在的。这就说明,一个成员变量加了final关键字后,JMM一定是做了相关处理的。final的两个重排序规则对于final域,编译器和处理器需要遵守两个重排序规则。1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。(写入时一定保证给final成员变量赋值在实例化完.原创 2021-04-05 14:41:41 · 192 阅读 · 0 评论 -
深入volatile关键字
volatile关键字的两层语义 一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。 禁止进行指令重排序。(某种意义上的有序性)现在有以下代码:class Test{ int a = 0; boolean flag = false; public void writer(){ a = 1; //原创 2021-04-05 13:49:53 · 108 阅读 · 2 评论 -
JMM(JAVA内存模型)和底层实现原理
什么是JMM(线程工作内存和主内存)Java Memory Model(Java内存模型), 围绕着并发过程中如何处理可见性、原子性、有序性这三个特性而建立的模型。从抽象的角度来看,JMM 定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是 JMM 的一个抽象概念,并不真实存在。它涵盖了缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。以下图原创 2021-04-05 01:33:13 · 866 阅读 · 2 评论 -
Callable,Future和FutureTask
Runnable是一个接口,在它里面声明了一个run()方法,由于run()方法返回值为void类型,所以在执行完任务之后无法返回任何结果。Callable位于java.util.concurrent包下,它也是一个接口。在它里面也只声明了一个方法,只不过这个方法叫做call(),这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。Future就是对于具体的Runnable或者Callable任务的执行结果进行取消,查询是否完成,获取结果。必要时可以通过get()方法获取执行结果,该方原创 2021-04-03 13:09:37 · 104 阅读 · 1 评论 -
线程的并发工具类
CountDownLatch闭锁闭锁,CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望,在所有启动框架服务的预备子线程执行完毕后再进行执行。CountDownLatch是通过一个计数器来实现的。计数器的初始值为初始人物的数量。每当完成了一个任务后,计数器的值就会减1。(CountDownLatch.countDown()方法)。当计数器值到达0时,它表示所有的子线程已经完成了任务。然后在闭锁上等待的CountDownLatch.awa原创 2021-04-03 12:22:31 · 133 阅读 · 0 评论 -
线程的并发框架—ForkJoin
Fork-JoinJava下多线程的开发可以使我们自己启用多线程,线程池,还可以使用Forkjoin框架。Forkjoin可以让我们不去了解诸多Thread,Runnable等相关知识。只要遵循forkjoin的开发模式,就可以写出很好的多线程并发程序。Fork-join的本质------分而治之,治完合并上报同时forkjoin在处理某一类问题时非常的有用。哪一类问题呢?分而治之的问题。计算机经典算法:快速排序,堆排序,归并排序,二分查找,线性查找,深度优先,广度优先,Dijkstra,动态规原创 2021-04-03 11:11:21 · 321 阅读 · 1 评论 -
老生常谈CAS
什么是原子操作?假定有两个操作A和B,如果从执行A线程来看,当另一个线程执行B时。那么需要将B全部执行完毕,要么完全不执行B。那么A和B彼此来说是原子的。如何实现原子操作?实现原子操作可是使用锁,锁机制,满足基本的需求是没问题了。但是有时候我们的需求并非这么简单,我们需要更有效,更加灵活的机制。Synchronized关键字是基于阻塞的锁机制。也就是说当一个线程拥有锁的时候,访问统一资源的其他线程需要等待,直到该线程释放锁。CAS之前的锁状态在JDK5之前Java语言是靠synchron原创 2021-04-02 22:07:21 · 181 阅读 · 0 评论 -
并发环境下,如何提高性能?
使用并发的目标是为了提高性能。引入多线程后,其实会引入额外的开销。如果线程之间的协调,增加的山下文切换,线程的创建和销毁,线程的调度等等。如果是CPU密集型任务却开了很多线程去执行,就会导致多线程程序比单线程还低。衡量应用的程序的性能:服务时间,延迟时间,吞吐量,节伸缩性等等。其中服务时间,延迟时间(多快),吞吐量(干活的时间占时间的比例)。此二者相互独立甚至互相矛盾。对服务器来说,吞吐量往往比执行速度更重要。因此1.保证程序正确,确实达不到要求再提升速度。2.一定要以测试为基准。(压测..原创 2021-04-02 16:55:14 · 574 阅读 · 0 评论 -
你的单例模式写对了么?
什么是单例模式单例模式是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。一般实现单例模式的方式有两种:懒汉式以及饿汉式。饿汉式public class SingleEHan饿汉式 { private SingleEHan饿汉式(){ } private原创 2021-04-02 16:35:52 · 102 阅读 · 2 评论 -
程序死锁了,我该怎么办?
什么是死锁?是指两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。若无外力作用,他们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。举例:A和B头部,洗脚按摩。现在有共有两个技师负责这两个部位的按摩。A和B的需求是两种按摩同时进行,A找到了头部按摩师,B找到了洗脚按摩师。此时A想继续让洗脚按摩师来,B也想继续让头部按摩师来。此时两人针锋相对,谁也不肯退让(程序的串行特性)。因此产生了死锁。总结:死锁的必然发生条件是多操作者(M>=2),争夺多资源(N原创 2021-04-02 14:32:35 · 486 阅读 · 2 评论 -
保证线程安全有哪些手段
什么是线程安全在《Java并发编程实战》中,定义如下:当多个线程访问某各类时,不管运行时环境采用何种调度方式或者这些线程如何交替执行,并且在调用代码中不需要额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。线程不安全的原因会从三方面进行考虑:就是原子性,可见性,有序性。在https://blog.csdn.net/weixin_47184173/article/details/115388962博客中会详细分析。保证线程安全的手段有哪些线程封闭实现好的并原创 2021-04-02 10:58:03 · 829 阅读 · 0 评论 -
Java线程不安全的原因
线程不安全的原因这个问题,一般在学Java SE时,我们老师会让我们背诵一段长长的话。"当不同线程同时能访问同一个变量时,可能会导致线程不安全"。实际上,这句话重点想突出的只有原子性。而我们往往考虑线程不安全的原因,会从三方面进行考虑:就是原子性,可见性,有序性。原子性即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。原子性主要针对的是对任务粒度的描述。因为在CPU的层面上来说。它不会因为某一个线程正在执行某部分代码块儿,就不给这个代码块儿再分配其他线原创 2021-04-02 07:35:49 · 575 阅读 · 0 评论 -
最硬核的AQS解析,你啃的下来么?
AQS是什么?很多初学者在刚接触AQS的时候,甚至可能在学习了一遍之后,还是觉得这个知识点有些虚无缥缈。因为太多太多的文章博客,上来就用AQS来讲ReentrantLock了,然后一顿学习后,诶?这和AQS有什么关系?首先,我们还是一步一步来由浅入深的去了解AQS。让我们穿越到过去,在JDK1.5以前只有synchronized同步锁,这个锁在那个时代是真的low。功能十分的局限,比如我想完成一个锁中的分组唤醒(Condition),锁的阻塞线程可打断,锁的公平功能等等。此外,速度也慢的一塌糊涂原创 2021-04-02 00:02:04 · 699 阅读 · 8 评论 -
AQS的前菜—详解CLH队列锁
什么是CLH队列锁CLH锁其实就是一种基于逻辑队列非线程饥饿的一种自旋公平锁。当多线程竞争一把锁时,获取不到锁的线程,会排队进入CLH队列的队尾,然后自旋等待,直到其前驱线程释放锁。由于是 Craig、Landin 和 Hagersten三位大佬的发明,因此命名为CLH锁。自旋锁与互斥锁的区别由于CLH锁是一种自旋锁,那么我们先来看看自旋锁是什么?自旋锁说白了也是一种互斥锁,只不过没有抢到锁的线程会一直自旋等待锁的释放,处于busy-waiting的状态,此时等待锁的线程不会进入休眠状态,而原创 2021-03-31 01:33:17 · 1701 阅读 · 2 评论 -
显式锁Lock的集大成之作,最细节教程
显示锁是什么?我们一般喊synchronized就叫synchronized。其实synchronized又被称为隐式锁,但我们就爱喊它synchronized。而显示锁就是我们一般说的Lock锁,但大家就是爱叫它显示锁。大概是Lock很容易和别的单词搞混吧?但无论如何,显示锁就是说的Lock锁。那么Lock为啥要叫它显示锁呢?八竿子打不着一边我很难记住啊!我们来看一下Lock加锁的范式:如上图,我们注意到,lock.unlock();是被放入 finally 代码块里的,这是为了保原创 2021-03-29 02:13:53 · 575 阅读 · 0 评论 -
Java并发编程—写时复制容器
面试官问什么是COW"靠?我来面试你怎么骂人呢?"。面试官:"不,不是那个靠。是英文COW。""......母牛?"。面试官汗颜......"你知道写时复制容器么?"通过这个小例子(非真实存在),加深一下各位对这个简称的印象。Copy-On-Write简称COW。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnW原创 2021-03-27 18:04:53 · 222 阅读 · 0 评论 -
简简单单的阻塞,非阻塞队列
什么是队列队列是一种非常常用的数据结构,一进一出,先进先出。阻塞队列与非阻塞队列非阻塞队列与阻塞队列,当然它们都是线程安全的,无需担心在多线程并发环境所带来的不可预知的问题。为什么会有非阻塞和阻塞之分呢?我们来假设在现在有一个有界的队列。有界非阻塞队列如果队列满的时候,像尾部再次进行插入会立刻返回插入失败,并且无视此次插入。如果队列空的时候,移除并获取队列首个元素会立刻返回移除失败,并无视此次移除操作。有界阻塞队列如果队列满的时候,像尾部再次进行插入会将该线程阻塞,直到.原创 2021-03-27 17:04:12 · 538 阅读 · 0 评论 -
一文搞定SkipList跳表
ConcurrentSkipListMap的使用场景有了ConcurrentHashMap了,我们还需要设计其他并发容器么?没错,ConcurrentHashMap无法保证我们存储数据的有序性。假设现在有这么一个需求。我们现在有一个教育系统,这个教育系统对应了无数学生上传自己的数学成绩。学生上传的数据规则为<key:分数,value:姓名>。然后我们的教育系统需要对所有的数据进行汇总并排序。此时我们的ConcurrentHashMap就有点束手无策了。我们的ConcurrentSki原创 2021-03-27 02:06:22 · 269 阅读 · 0 评论 -
并发环境下的Map面试题汇总
Q:HashMap和Hashtable的区别?1.HashMap是线程不安全的,HashTable是线程安全的。2.由于线程安全,所以HashTable的效率比不上HashMap。3.HashMap最多只允许一条记录的键为null,允许多条记录的值为null,而HashTable不允许。4.HashMap默认初始化数组的大小为16,HashTable为11。前者扩容时扩大两倍,后者扩大两倍+1。5.HashMap需要重新计算hash值,而HashTable直接使用对象的hash...原创 2021-03-26 23:33:04 · 121 阅读 · 0 评论 -
线程间的协作——等待唤醒机制
等待/通知机制等待唤醒机制就是多个线程间的一种协作机制,谈到线程我们经常想到的是线程间的竞争,比如各个线程争夺锁,但是多个线程也会有协作的机制,就好比在公司中你与你的同事是竞争关系,但是你们也会合作完成某个项目等待唤醒机制就是在一个线程进行了规定操作后,另一个线程就进入等待状态(wait()),等待刚刚进行规定操作的线程执行完他们的指定代码后,再将处于等待状态的这个线程唤醒(notify());在有多个线程进行等待的时候,如果需要我们可以使用notifyAll()来唤醒所有的等待线程。wait/原创 2021-03-26 09:29:30 · 365 阅读 · 0 评论 -
线程池有这么多细节,你都了解么
为什么要用线程池Java中的线程池是运用场景最多的并发框架。几乎所有需要异步或者并发执行的任务都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。第一:降低资源消耗。通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。第二:提高效应速度。当任务产生时,可以不需要等到线程创建就立即执行。一个服务器完成一项任务所需时间为:T1创建线程时间,T2在线程中执行任务的时间,T3销毁线程时间。如果T1+T3的时间远大于T2,则可以采用线程池,以提高服务器性能。线程池技术正是关注如何缩短或调原创 2021-03-25 09:09:12 · 470 阅读 · 2 评论 -
进程,线程与CPU之间是如何搭伙儿过小日子的
进程与线程的概念进程和线程的概念已经被大家讲烂了。网上一般是这样定义的:进程是资源分配的最小单位,线程是处理器调度的最小单位。这么说,你听懂了吗?我觉得这样的定义纯粹是自说自话:新手看完了一脸懵,老鸟看完了不以为然。本文带你深刻认识,进程与线程的概念,以及与CPU是如何配合干活儿的。CPU核心数与线程的关系首先先说一些基础的概念。一个计算机只有一个CPU,这是勿容置疑的。在不断地学习多线程的知识后,有些朋友已经脑袋里是一堆CPU在计算机里到处乱转了。实际上它只有一个。诶?那不是说原创 2021-03-23 01:00:32 · 463 阅读 · 0 评论 -
认识Java里的线程,谈谈线程的它的启动与假中止(interrupt)
Java程序天生就是多线程的一个Java程序从main()方法开始执行,然后按照既定的代码逻辑执行,看似没有其他线程参与,但实际上Java程序天生就是多线程程序。因为执行main()方法的是一个名称为main的线程。[1]main //main线程,用户程序入口[2]Monitor Ctrl-Break //监控Ctrl-Break中断信号[3]AttachListener //内存dump,线程dump,类信息统计,获取系统属性等[4]SignalDispatcher/...原创 2021-03-23 00:59:37 · 178 阅读 · 1 评论 -
Java线程的6种状态与相关方法
Java线状态详细说明1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中。此时虽然尚未获得CPU的使用权,但已处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。3.阻塞(...原创 2021-03-23 00:58:06 · 278 阅读 · 1 评论 -
多线程的优势与好处
由于多核多线程的CPU诞生,高并发的编程越来越受关注,多线程可以给程序带来的好处如下:充分利用CPU资源从上面的CPU介绍,现在市面上没有CPU的内核不适用多线程并发机制的。特别是服务器还不止一个CPU,如果还是使用单线程的技术做思路,明显就OUT了。因为程序员的基本调度单元是线程,并且一个线程也只能在一个CPU的一个核的一个线程跑。如果你是i3的CPU的话,最差也是双核心4线程的运算能力。如果是一个线程的程序的话,那是要浪费3/4的CPU性能:如果设计一个多线程程序的话,那它就可以同时在多个CPU原创 2021-03-23 00:56:31 · 2749 阅读 · 1 评论 -
一个小例子秒懂并发/并行的区别
非并行非并发吃饭吃了一半,电话来了,你一直吃饭吃完了以后才去接,这说明你不支持并发也不支持并行。并发吃饭吃了一半,电话来了,你停下来接了电话,接完继续吃饭,说明你支持并发。(此处不强调在时间点上同时进行,重点是一段时间内可以交替执行)。并行吃饭吃了一半,你一边接电话一边吃饭,这说明你支持并行。总结此时不难发现,并行概念可以归纳为并发概念的一个子集并发的关键在于你有处理多个任务的能力,不一定要同时,只要能交替执行。比如单CPU核心下执行多线程并非是同时执行多个任务,如果你开原创 2021-03-23 00:55:55 · 2767 阅读 · 0 评论 -
简单轻松地秒杀ConcurrentHashMap
HashMap线程不安全怎么办?我们都知道HashMap结合了数组与链表(红黑树)的特性,可以快速的进行取值存值。然而他最大的弊端便是不能在多线程环境下使用(会造成值覆盖与循环链表)。映射到我们的代码中,最常见的就是我们不能写static Map map = new HashMap();这样的语法,供多个线程使用。那么,在多线程环境下,我们该如何选择key-value键值对格式的数据结构呢?Hashtable,为所有数据添加重量级锁JDK的命名bugHashtable这个命名就很有意思,原创 2021-03-21 04:05:21 · 579 阅读 · 4 评论 -
面试必问的HashMap,如何交上完美答卷
HashMap的实现原理图本文所有内容皆围绕着HashMap1.7进行讲解。可能后续会省略1.7的版本号,望读者知晓。在JDK1.7中,HASHMAP是由数组+链表实现的,原理图如下:HashMap的设计初衷在详细讲解HashMap之前,我依旧认为,要想彻底理解一种数据结构,必须要从它的存在意义的角度开始理解。它为什么产生,与它的产生带来了怎样的意义。因此,我们现在假设自己穿越了时空,回到那个还没有HashMap的时代。假设,我们现在需要实现一个key-value键值对这种数据结构的原创 2021-03-18 15:50:41 · 392 阅读 · 10 评论 -
还不理解ThreadLocal?那是你还没读到这篇文章
与Synchonized的比较,它的作用是什么ThreadLocal和Synchonized都用于解决多线程并发访问。可是ThreadLocal与Synchronized有着本质的区别。Synchronized是利用锁的机制,使变量或代码代码块在某一个时刻仅仅能被一个线程访问。从名字我们就可以看到ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部原创 2021-01-19 23:28:57 · 520 阅读 · 2 评论