GVKun编程网logo

微信小程序商城高并发解决方案(小程序如何解决高并发)

20

关于微信小程序商城高并发解决方案和小程序如何解决高并发的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于java高并发解决方案、java并发编程与高并发解决方案、Java并发编程与高并发解决

关于微信小程序商城高并发解决方案小程序如何解决高并发的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于java 高并发解决方案、java并发编程与高并发解决方案、Java并发编程与高并发解决方案(一)-并发与高并发基本概念、java高并发解决方案等相关知识的信息别忘了在本站进行查找喔。

本文目录一览:

微信小程序商城高并发解决方案(小程序如何解决高并发)

微信小程序商城高并发解决方案(小程序如何解决高并发)

如何优化商城高并发服务?对于线上服务会有哪些挑战?

①无法做离线缓存,所有数据都是实时读取。

②会有大量的请求发送给在线服务,对服务的响应时间要求较高,一般限制在300ms以内。如果超过这个时间,用户体验会急剧下降。

③数据量大。如果单次qps超过50W,单条1kb,50万就是5GB了,1分钟30G,对底层数据存储和访问压力很大。本文将讨论如何处理这些棘手的问题。

一、向关系型数据库sayno

真正大规模的面向互联网C端的服务,是不会直接把数据库作为自己的存储系统的。无论是使用底层的子数据库和子表,还是各种优秀的连接池,MysqL/oracle面对大规模的在线服务都有着天然的劣势。再怎么优化,也难以抵挡qps超过50万流量的冲击。所以换个思路,我们必须使用类似nosql的缓存系统,比如redis/mermCache,作为自己的“数据库”,而MysqL等关系型数据库只是异步编写数据查询的备份系统。

就比如在双11主会场http://JD.COM,一些商品被摆上货架。这些商品在会场上架时直接写入redis,上架后再通过异步消息写入MysqL。c端查询总是直接读取redis,而不是数据库,而B端查询可以去数据库。这部分流量不是很高,数据库肯定能承受。

二、使用多级缓存的优势

最近大家都知道CRMEB PRO商城是专门为高并发打造的,那么大家也都知道缓存是提高高并发性能的利器之一。那么如何在CRMEB PRO商城系统上利用好缓存,怎么在CRMEB PRO商城系统上面利用多级缓存,是我们需要思考的问题。Redis是目前缓存的首选。单机可以达到6-8万qps。面对高并发,我们可以手动横向扩展容量,满足qps可能无线增长的场景。但是这种方式也有缺点,因为redis是单线程的,会有热点问题。虽然redis内部使用crc16算法哈希,但是同样的密钥还是会落在单独的机器上,增加机器的负载。redis典型的有两个问题:缓存击穿和缓存穿透,尤其是在秒杀场景下,如果想要解决热点问题,会变得更加困难。这时候就必须考虑多级缓存了。在典型的峰值场景中,单个sku商品的qps将在销售开始的那一刻急剧上升。而我们这个时候需要用memeryCache来阻挡一层。memeryCache是多线程的,并发性比redis好,自然能解决热点问题。有了memeryCache,我们还需要localCache,这是一种用内存换取速度的方式。本地缓存将访问用户的一级请求。如果它找不到,就去memeryCache,然后redis。这个过程可以阻止数百万个qps。

三、多线程很牛,但也要适当使用

多线程有这么厉害吗?干嘛都说多线程,为什么CRMEB PRO商城系统要使用多线程,不用行不行?要讲明这个道理,我先来说一个实例.很典型的一个场景。原始的方式是循环一个30-40万的list,list执行的操作很简单,就是读redis中的数据,读一次数据一般需要3ms左右,这是同步的方式,在预览环境测试,是30秒+超时。我们优化的方式就是把同步调用改为线程池调用,线程池里的线程数或阻塞队列大小需要自己调优,最后实测接口rt只需要3秒。足以见多线程的强大。在多核服务的今天,如果还不使用多线程那就是对服务器资源浪费。这里需要说一句,使用多线层一定要做好监控,你需要随时知道线程的状态,如果线程数和queueSize设置的不恰当,将会严重影响业务,当然多线程也要分场景的,如果为了多线程而多线程反而更是一种资源浪费,因为多线程调度的时候会造成线程在内核态和用户态之间来回切换,如果使用不当反而会有反作用

四、降级和熔断可以带来不一样的效果

降级熔断是一种自我保护措施,这和电路上熔断的基本原理是一样的。可以防止电流过大引起火灾等。面对不可控的巨大流量请求,很可能会破坏服务器的数据库或redis,导致服务器宕机或瘫痪,造成无法挽回的损失。因为我们的服务本身需要有一个防御机制来抵御外部服务对自身的入侵,造成服务的破坏和联合反应。降级不同于熔断。两者的区别在于降级是在不影响主链接的情况下,关闭一些网上主链接的功能。如果熔断,意味着A请求B,B检测到业务流量有多大,开始熔断,那么请求会直接进入熔断池,直接返回失败。如何选择使用哪一种,需要在实践中结合业务场景来考虑。

五、优化IO让商城更加丝滑

很多人都会忽视IO这个问题,频繁的建联和断联都是对系统的重负。在并发请求中,如果存在单个请求的放大效那么将会使io呈指数倍增加。比如主会场的商品信息,如果需要商品的某个具体的详情,而这个详情需要调用下游来单个获取.随着主会场商品的热卖,商品越来越多,一次就要经过商品数X下游请求的数量,在海量的qps请求下,IO数被占据,大量的请求被阻塞,接口的响应速度就会呈指数级下降。所以需要批量的请求接口,所有的优化为一次IO

六、慎用重试,也特别注意以下几点!

重试是处理临时异常的常用方法。常见的处理方法是请求服务失败或写入数据库并重试。使用重试时,必须注意以下几点:①控制重试次数;②测量重试间隔;③是否重试做到配置化。之前我们线路上有个bug。kafka的消费有严重的滞后性,一个词的消耗时间在10秒以上。看了代码后发现是重试次数太多导致的,次数多不支持配置修改,所以当时的做法只能是临时改代码再上线。重试作为一个业务的第二次尝试,大大提高了程序的请求成功,但也要注意以上几点。

七、边界case的判断和兜底也很重要

作为互联网老手,很多人写出了不错的代码,但是经过几轮故障审查,发现很多造成重大事故的代码背后都是对一些边界问题的处理不足。他们犯的错误非常简单,但往往是这些小问题导致了重大事故。一次重大事故回顾,后来发现最终原因是没有判断空数组,导致传入的下游rpc为空,下游直接返回了全量业务数据,影响了上百万用户。这段代码修改起来很简单,但是需要自省,小的不足导致大的灾难。

八、学会优雅的打印日志,让商城问题无处遁形

作为跟踪在线问题的最佳工具,日志是保留bug场景的唯一来源。虽然有arthas这样的工具帮助我们排查问题,但是对于一些复杂的场景,还是需要日志来记录程序的数据。但在高流量场景下,如果打印全部日志对于online来说是一场灾难,有以下几个缺点:①磁盘占用严重。据估计,如果接口的qps在200000左右,则日志将为每秒几千兆字节,一天就有几千GB。②需要输出大量日志。占用了程序IO,增加了接口的RT(响应时间)。如果需要解决这个问题,①可以利用限流元件实现一个基于限流的对数元件。令牌桶算法可以限制打印日志的流量,比如一秒钟只能打印一个日志。②基于白名单的日志打印只有在线白名单用户才能打印,节省了大量无效日志输出。

总结:

本文讨论了CRMEB PRO商城系统高并发服务在面对大流量时的一些技术手段和应对要点。当然,实际的在线服务要比现在的复杂。这里只是一些建议。希望能保持敬畏之心,在高并发的道路上继续探索。更好的培养C端服务,做出更好的互联网商城应用。

java 高并发解决方案

java 高并发解决方案

一个小型的网站,比如个人网站,可以使用最简单的 html 静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统 架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术 更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的 html 静态网站所能比 拟的。

大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的 Web 容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。

上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。

1、HTML 静态化

其实大家都知道,效率最高、消耗最小的就是纯静态化的 html 页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最 有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统 CMS,像我们常访问的各个门户站点的 新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管 理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的 CMS 是必不可少的。

除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像 Mop 的大杂烩就是使用了这样的策略,网易社区等也是如此。

同时,html 静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用 html 静态化来实现,比如论坛中 论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部 分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。

2、图片服务器分离

大家知道,对于 Web 服务器来说,不管是 Apache、IIS 还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网 站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图 片问题而崩溃,在应用服务器和图片服务器上,可以进行不同的配置优化,比如 apache 在配置 ContentType 的时候可以尽量少支持,尽可能少的 LoadModule,保证更高的系统消耗和执行效率。

3、数据库集群和库表散列

大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。

在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase 等都有很好的方案,常用的 MySQL 提供的 Master/Slave 也是类似的方案,您使用了什么样的 DB,就参考相应的解决方案来实施即可。

上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用 DB 类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且 最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功 能进行更小的数据库散列,比如用户表,按照用户 ID 进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu 的论坛就是采用了这样的架 构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和 ID 进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统 随时增加一台低成本的数据库进来补充系统性能。

4、缓存

缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。 
架构方面的缓存,对 Apache 比较熟悉的人都能知道 Apache 提供了自己的缓存模块,也可以使用外加的 Squid 模块进行缓存,这两种方式均可以有效的提高 Apache 的访问响应能力。 
网站程序开发方面的缓存,Linux 上提供的 Memory Cache 是常用的缓存接口,可以在 web 开发中使用,比如用 Java 开发的时候就可以调用 MemoryCache 对一些数据进行缓存和通讯共享,一些大 型社区使用了这样的架构。另外,在使用 web 语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP 有 Pear 的 Cache 模块,Java 就更多 了,.net 不是很熟悉,相信也肯定有。

5、镜像

镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如 ChinaNet 和 EduNet 之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现 成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如 Linux 上的 rsync 等工具。

6、负载均衡

负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。

负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。

1)硬件四层交换

第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。 第四层交换功能就象是虚 IP,指向物理服务器。它传输的业务服从的协议多种多样,有 HTTP、FTP、NFS、Telnet 或其他协议。这些业务在物理服 务器基础上,需要复杂的载量平衡算法。在 IP 世界,业务类型由终端 TCP 或 UDP 端口地址来决定,在第四层交换中的应用区间则由源端和终端 IP 地址、 TCP 和 UDP 端口共同决定。

在硬件四层交换产品领域,有一些知名的产品可以选择,比如 Alteon、F5 等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。Yahoo 中国当初接近 2000 台服务器使用了三四台 Alteon 就搞定了。

2)软件四层交换

大家知道了硬件四层交换机的原理后,基于 OSI 模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。

软件四层交换我们可以使用 Linux 上常用的 LVS 来解决,LVS 就是 Linux Virtual Server,他提供了基于心跳线 heartbeat 的实时灾难应对解决方案,提高系统的鲁棒性,同时可供了灵活的虚拟 VIP 配置和管理功能,可以同时满 足多种应用需求,这对于分布式的系统来说必不可少。

一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建 squid 集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。


java并发编程与高并发解决方案

java并发编程与高并发解决方案

下面是我对java并发编程与高并发解决方案的学习总结:

1、并发编程的基础

2、线程安全—可见性和有序性

3、线程安全—原子性

4、安全发布对象—单例模式

5、不可变对象

6、线程封闭

7、线程不安全类

8、同步容器

9、J.U.C之AQS

10、线程的几种创建方式

11、FutureTask、Fork/Join、 BlockingQueue

12、线程池

13、死锁

14、多线程并发最佳实践

15、spring的线程安全

16、HashMap与ConcurrentHashMap

17、多线程并发与线程安全总结

18、高并发之扩容思路

19、高并发之缓存

20、高并发之消息队列

21、高并发之应用的拆分

22、高并发之应用限流

23、高并发之服务降级和服务熔断

24、数据库切库、分库、分表

25、高并发之高可用

26、并发编程总结

Java并发编程与高并发解决方案(一)-并发与高并发基本概念

Java并发编程与高并发解决方案(一)-并发与高并发基本概念

并发:

    多个线程操作相同的资源,保证线程安全,合理利用资源

高并发:

    服务能同时处理很多请求(如12306的抢票,天猫双十一的秒杀活动,这会导致系统在短时间内执行大量的操作,如对资源的请求,数据库的访问),提高程序性能(如果高并发处理不好,不光会导致用户体验不好,还可能会使服务器宕机,出现内存泄露等问题)

 

java高并发解决方案

java高并发解决方案

转载https://blog.csdn.net/GavinZhera/article/details/86471828

<divid="content_views"> <h1><a name="t0"></a><a name="t0"></a>知识点</h1>

<p>线程安全,线程封闭,线程调度,同步容器,并发容器,AQS,J.U.C,等等</p>

<h1><a name="t1"></a><a name="t1"></a>高并发解决思路与手段</h1>

<p>扩容:水平扩容、垂直扩容</p>

<p>缓存:Redis、Memcache、GuavaCache等</p>

<p>队列:Kafka、RabitMQ、RocketMQ等</p>

<p>应用拆分:服务化Dubbo与微服务Spring Cloud</p>

<p>限流:Guava RateLimiter使用、常用限流算法、自己实现分布式限流等</p>

<p>服务降级与服务熔断:服务降级的多重选择、Hystrix</p>

<p>数据库切库,分库分表:切库、分表、多数据源</p>

<p>高可用的一些手段:任务调度分布式elastic-job、主备curator的实现、监控报警机制</p>

<h1><a name="t2"></a><a name="t2"></a>基础知识与核心知识准备</h1>

<p>并发高并发相关概念</p>

<p>cpu多级缓存:缓存一致,乱序执行优化</p>

<p>java内存模型:JMM规定,抽象结构,同步操作与规则</p>

<p>并发优势与风险</p>

<p>并发模拟:Postman,Jmeter,Apache Bench,代码</p>

<h1><a name="t3"></a><a name="t3"></a>并发基本概念</h1>

<p>同时拥有两个或多个线程,如果程序在单核处理器上运行,多个线程将交替的换入或者换出内存,这些线程是同时“存在”的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上,此时,程序中的每个线程都将分配到一个处理器核上,因此可以同时运行。</p>

<h1><a name="t4"></a><a name="t4"></a>高并发基本概念</h1>

<p>高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够<span>同时并行处理</span>很多请求。</p>

<p><strong>并发:</strong>多个线程操作相同的资源,保证线程安全,合理使用资源</p>

<p><strong>高并发:</strong>服务能同时处理很多请求,提高程序性能(更多的考虑技术手段)</p>

<h1><a name="t5"></a><a name="t5"></a>知识技能</h1>

<p>总体架构:Spring Boot、Maven、JDK8、MySQL</p>

<p>基础组件:Mybatis、Guava、Lombok、Redis、Kafka</p>

<p>高级组件:Joda-Time、Atomic包、J.U.C、AQS、ThreadLocal、RateLimiter、Hystrix、ThreadPool、Shardbatis、curator、elastic-job等</p>

<h1><a name="t6"></a><a name="t6"></a>基础知识</h1>

<h2><a name="t7"></a><a name="t7"></a>cpu多级缓存</h2>

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

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

<p><strong>CPU cache有什么意义:</strong></p>

<p>1)时间局部性:如果某个数据被访问,name在不久的将来它很可能被再次访问。</p>

<p>2)空间局部性:如果某个数据被访问,那么与它相邻的数据很快也可能被访问</p>

<h2><a name="t8"></a><a name="t8"></a>CPU多级缓存-缓存一致性(MESI)</h2>

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

<p>缓存四种操作:local read、local write、remote read、remote write</p>

<h1><a name="t9"></a><a name="t9"></a>CPU多级缓存-乱序执行优化</h1>

<p>在多核处理器上回出现问题</p>

<h1><a name="t10"></a><a name="t10"></a>java内存模型(java memory model,JMM)</h1>

<h2><a name="t11"></a><a name="t11"></a>java内存模型-同步八种操作</h2>

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

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

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

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

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

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

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

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

<h2><a name="t12"></a><a name="t12"></a>java内存模型-同步规则</h2>

<ol><li>如果要把一个变量从主内存中复制到工作内存,就需要按顺序的执行read和load操作,如果把变量从工作内存中同步回主内存,就需要按顺序的执行store和write操作。但java内存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行</li> <li>不允许read和load、store和write操作之一单独出现</li> <li>不允许一个线程丢弃它的最近assign的操作,即变量在工作内存中改变了之后必须同步到主内存中</li> <li>不允许一个线程无原因的(没发生过任何assign操作)把数据从工作内存同步回主内存中</li> <li>一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或assign)的变量。即就是对一个变量实施use和store操作之前,必须先执行过了assign和load操作</li> <li>一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现</li> <li>如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量之前需要重新执行load或assign操作初始化变量的值</li> <li>如果一个变量实现没有被lock操作锁定,怎不允许对它执行unlock操作,也不允许去unlock一个被其他线程锁定的变量</li> <li>对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)</li> </ol><h1><a name="t13"></a><a name="t13"></a>并发的优势与风险</h1>

<h2><a name="t14"></a><a name="t14"></a>优势</h2>

<p>速度:同时处理多个请求,响应更快;复杂的操作可以分成多个进程同时进行</p>

<p>设计:程序设计在某些情况下更简单,也可以更多的选择</p>

<p>资源利用:CPU能够在等待IO的时候做一些其他的事情</p>

<h2><a name="t15"></a><a name="t15"></a>风险</h2>

<p>安全性:多个线程共享数据时可能会产生于期望不相符的结果</p>

<p>活跃性:某个操作无法继续进行下去时,就会发生活跃性问题。比如死锁、饥饿等问题</p>

<p>性能:线程过多时会使得CPU频繁切换,调度时间增多;同步机制;消耗过多内存</p>

<p>&nbsp;</p>

<h1><a name="t16"></a><a name="t16"></a>线程安全性</h1>

<p>定义:当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类时线程安全的。</p>

<p><strong>线程安全体现在以下三个方面</strong></p>

<p>原子性:提供了互斥访问,同一时刻只能有一个线程来对他进行操作</p>

<p>可见性:一个线程对主内存的修改可以及时的被其他线程观察到</p>

<p>有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序</p>

<h2><a name="t17"></a><a name="t17"></a>原子性——Atomic包</h2>

<p><span>AtomicXxxx:CAS、Unsafe.compareAndSwapInt</span></p>

<p><span>AtomicXxxx</span>类中方法incrementAndGet(),incrementAndGet方法中调用<span>unsafe</span>.getAndAddInt(),getAndAddInt方法中主题是do-while语句,while语句中调用<span>compareAndSwapInt</span>(var1, var2, var5, var5 + var4)</p>

<p><span>compareAndSwapInt方法就是CAS核心:</span></p>

<p><span>在死循环内,不断尝试修改目标值,直到修改成功,如果竞争不激烈,修改成功率很高,否则失败概率很高,性能会受到影响</span></p>

<p><span>jdk8中新增LongAdder,它和AtomicLong比较</span></p>

<p><span>优点:性能好,在处理高并发情况下统计优先使用LongAdder</span></p>

<p><span>AtomicReference、AtomicReferenceFieldUpdater原子性更新字段(字段要求volatile修饰,并且是非static)</span></p>

<p>AtomicStampReference:CAS的ABA问题</p>

<p>ABA问题:变量已经被修改了,但是最终的值和原来的一样,那么如何区分是否被修改过呢,用版本号解决</p>

<p>AtomicBoolean可以让某些代码只执行一次</p>

<h2><a name="t18"></a><a name="t18"></a>原子性——锁</h2>

<p><strong>synchronized:</strong>依赖jvm,作用对象的作用范围内</p>

<p>修饰代码块:同步代码块,大括号括起来的代码,作用于调用的对象</p>

<p>修饰方法:同步方法,整个方法,作用于调用的对象</p>

<p>修饰静态方法:整个静态方法,作用于所有对象</p>

<p>修饰类:括号括起来的部分,作用于所有对象</p>

<p><strong>Lock:</strong>依赖特殊CPU指令,代码实现,ReentrantLock</p>

<h2><a name="t19"></a><a name="t19"></a>原子性——对比</h2>

<p>synchronized:不可中断锁,适合竞争不激烈,可读性好</p>

<p>Lock:可中断锁,多样化同步,竞争激烈时能维持常态</p>

<p>Atomic:竞争激烈时能维持常态,比Lock性能好,只能同步一个值</p>

<h2><a name="t20"></a><a name="t20"></a>可见性</h2>

<p>导致共享变量在线程间不可见的原因:</p>

<p>1 线程交叉执行</p>

<p>2 重排序结合线程交叉执行</p>

<p>3 共享变量更新后的值没有在工作内存与主内存间及时更新</p>

<h2><a name="t21"></a><a name="t21"></a>可见性——synchronized</h2>

<p>JMM关于synchronized的两条规定:</p>

<p>线程解锁前,必须把共享变量的最新值刷新到主内存</p>

<p>线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意,加锁和解锁是同一把锁)</p>

<h2><a name="t22"></a><a name="t22"></a>可见性——volatile</h2>

<p>通过加入内存屏障和禁止重排序优化来实现</p>

<p>1 对volatile变量写操作时,会在写操作后加入一条store屏障指令,将本地内存中的共享变量值刷新到主内存</p>

<p>2 随volatile变量度操作时,会在读操作前加入一条load屏障指令,从主内存中读取共享变量</p>

<p>使用volatile修饰变量,无法保证线程安全</p>

<p>volatile适合修饰状态标识量</p>

<h2><a name="t23"></a><a name="t23"></a>有序性</h2>

<p>java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性</p>

<h2><a name="t24"></a><a name="t24"></a>有序性——happens-before原则</h2>

<p>1 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作</p>

<p>注:在单线程中,看起来是这样的,虚拟机可能会对代码进行指令重排序,虽然重排序了,但是运行结果在单线程中和指令书写顺序是一致的,事实上,这条规则是用来保证程序单在单线程中执行结果的正确性,无法保证程序在多线程中的正确性</p>

<p>2 锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作</p>

<p>3 volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作</p>

<p>4 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C</p>

<p>前四条规则比较重要</p>

<p>5 线程启动规则:Thread对象的start()方法先行发生于次线程的每一个动作</p>

<p>6 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码监测到中断事件的发生</p>

<p>7 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行</p>

<p>8 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始</p>

<h2><a name="t25"></a><a name="t25"></a>线程安全性——总结</h2>

<p>原子性:Atomic包、CAS算法、synchronized、Lock</p>

<p>可见性:synchronized、volatile</p>

<p>有序性:happens-before规则</p>

<p>一个线程观察其他线程指令执行顺序,由于重排序的存在,观察结果一般是无序的,如果两个操作执行顺序无法从happens-before原则推导出来,name他们就不能保证有序性,虚拟机可以随意的对他们重排序</p>

<h1><a name="t26"></a><a name="t26"></a>发布对象</h1>

<p>发布对象:使一个对象能够被当前范围之外的代码所使用</p>

<p>对象逸出:一种错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见</p>

<h2><a name="t27"></a><a name="t27"></a>安全发布对象四种方法</h2>

<p>1 在静态初始化函数中初始化一个对象引用</p>

<p>2 将对象的引用保存到volatile类型域或者AtomicReference对象中</p>

<p>3 将对象的引用保存到某个正确构造对象的final类型域中</p>

<p>4 将对象的引用保存到一个由锁保护的域中</p>

<p>私有构造函数,单例对象,静态工厂方法获取对象</p>

<p><strong>以单例模式为例</strong></p>

<p><strong>懒汉模式:</strong>单例实例在第一次使用时进行创建(线程不安全)</p>

<p>懒汉模式也可以实现线程安全,给getInstance方法添加synchronized关键字(不推荐,因为性能不好)</p>

<p>双重同步锁单例模式:双重监测机制,在方法内部加synchronized关键字(不是线程安全的)</p>

<p>原因是,创建对象是,分为以下三个步骤:</p>

<p>1) memory = allocate() 分配对象的内存空间</p>

<p>2)ctorInstance() 初始化对象</p>

<p>3)instance = memory() 设置instance指向刚分配的内存</p>

<p>由于JVM和cpu优化,可能会发生指令重排:</p>

<p>1) memory = allocate() 分配对象的内存空间</p>

<p>3) instance = memory() 设置instance指向刚分配的内存</p>

<p>2) ctorInstance() 初始化对象</p>

<p>当以上面这种指令执行时,线程A执行到3 instance = memory() 设置instance指向刚分配的内存 这一步时,线程B执行if(instance == null)这段代码,此时instance != null,线程B直接return instance,导致对象没有初始化完毕就返回</p>

<p><strong>解决办法就是限制对象创建时进行指令重排,volatile+双重监测机制-&gt;禁止指令重排引起非线程安全</strong></p>

<p><strong>饿汉模式:</strong>单例实例在类装载时进行创建(线程安全)</p>

<p><strong>枚举模式:</strong>线程安全</p>

<h1><a name="t28"></a><a name="t28"></a>不可变对象</h1>

<p>不可变对象需要满足的条件:</p>

<p>对象创建以后其状态就不能修改</p>

<p>对象所有域都是final类型</p>

<p>对象是正确创建的(在对象创建期间,this引用没有逸出)</p>

<p>参考String类型</p>

<h2><a name="t29"></a><a name="t29"></a>final关键字定义不可变对象</h2>

<p>修饰类、方法、变量</p>

<p>修饰类:不能被继承</p>

<p>修饰方法:1.锁定方法不被继承类修改 2.效率</p>

<p>修饰变量:基本数据类型,数值不可变;引用类型变量,不能再指向另外一个对象,因此容易引起线程安全问题</p>

<p>其他实现不可变对象</p>

<p>Collections.unmodifiableXXX:Collection、List、Set、Map(线程安全)</p>

<p>Guava:ImmutableXXX:Collection、List、Set、Map</p>

<h1><a name="t30"></a><a name="t30"></a>线程封闭性</h1>

<p>线程封闭概念:把对象封装到一个线程里,只有这个线程可以看到该对象,那么就算该对象不是线程安全的,也不会出现任何线程安全方面的问题。实现线程封闭的方法:</p>

<p>1 Ad-hoc线程封闭:程序控制实现,最糟糕,忽略</p>

<p>2 堆栈封闭:局部变量,无并发问题</p>

<p>3 threadLocal是线程安全的,做到了线程封闭</p>

<p>ThreadLocal内部维护了一个map,map的key是每个线程的名称,map的值是要封闭的对象,每一个线程中的对象都对应者一个map中的值</p>

<p>线程封闭的应用场景:</p>

<p>数据库连接jdbc的Connection对象</p>

<h1><a name="t31"></a><a name="t31"></a>线程不安全类与写法</h1>

<p>字符串</p>

<p>StringBuilder:线程不安全</p>

<p>StringBuffer:线程安全</p>

<p>时间转换</p>

<p>SimpleDateFormat:线程不安全</p>

<p>JodaTime:线程安全</p>

<p>集合</p>

<p>ArrayList:线程不安全</p>

<p>HashSet:线程不安全</p>

<p>HashMap:线程不安全</p>

<p>编程注意:</p>

<p>if(condition(a)){handle(a)}; 不是线程安全的,因为这条判断语句不是原子性的,如果有线程共享这条代码,则会出现并发问题,解决方案是想办法这这段代码是原子性的(加锁)</p>

<h1><a name="t32"></a><a name="t32"></a>线程安全——同步容器(在多线程环境下不推荐使用)</h1>

<p>ArrayList -&gt; Vector, Stack</p>

<p>Vector中的方法使用synchronized修饰过</p>

<p>Stack继承Vector</p>

<p>HashMap -&gt; HashTable(key、value不能为null)</p>

<p>HashTable使用synchronized修饰方法</p>

<p>Collections.synchronizedXXX(List、Set、Map)</p>

<p><span>同步容器不完全是线程安全的</span></p>

<p><strong>编程注意:</strong>如果使用foreach或者iterator遍历集合时,尽量不要对集合进行修改操作</p>

<h2><a name="t33"></a><a name="t33"></a>线程安全——并发容器J.U.C(java.util.concurrent)(在多线程环境下推荐使用)</h2>

<p>ArrayList -&gt; <strong>CopyOnWriteArrayList:</strong>相比ArrayList,CopyOnWriteArrayList是线程安全的,写操作时复制,即当有新元素添加到CopyOnWriteArrayList时,先从原有的数组里拷贝一份出来,然后在新的数组上写操作,写完之后再将原来的数组指向新的数组,CopyOnWriteArrayList整个操作都是在锁(ReentrantLock锁)的保护下进行的,这么做主要是避免在多线程并发做add操作时复制出多个副本出来,把数据搞乱了。第一个缺点是做写操作时,需要拷贝数组,就会消耗内存,如果元素内容比较多会导致youngGC或者是fullGc;第二个缺点是不能用于实时读的场景,比如拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到的数据可能还是旧的,虽然CopyOnWriteArrayList能够做到最终的一致性,但是没法满足实时性要求,因此CopyOnWriteArrayList更适合读多写少的场景</p>

<p>CopyOnWriteArrayList设计思想:1读写分离 2最终一致性 3使用时另外开辟空间解决并发冲突</p>

<p>HashSet -&gt; <strong>CopyOnWriteArraySet</strong></p>

<p>TreeSet -&gt; <strong>ConcurrentSkipListSet</strong></p>

<p><strong>CopyOnWriteArraySet:底层实现是CopyOnWriteArrayList</strong></p>

<p><strong>ConcurrentSkipListSet:和</strong>TreeSet 一样<strong>支持自然排序,基于map集合,但是批量操作不是线程安全的</strong></p>

<p>HashMap -&gt; <strong>ConcurrentHashMap</strong>&nbsp;:不允许空值,针对读操作做了大量的优化,具有特别高的并发性</p>

<p>TreeMap&nbsp; -&gt; <strong>ConcurrentSkipListMap </strong>:内部使用SkipList跳表结构实现的,key是有序的,支持更高的并发</p>

<h2><a name="t34"></a><a name="t34"></a>安全共享对象策略——总结</h2>

<p>1 线程限制:一个呗线程限制的对象,由线程独占,并且只能被占有它的线程修改</p>

<p>2 共享只读:一个共享只读的对象,在没有额外的同步情况下,可以被多个线程并发访问,但是任何线程都不能修改它</p>

<p>3 线程安全对象:一个线程安全的对象或容器,在内部通过同步机制来保证线程安全,所以其他线程无序额外的同步就可以通过公共接口随意访问它</p>

<p>4 被守护对象:被守护对象只能通过获取特定的锁来访问</p>

<p><span>不可变对象、线程封闭、同步容器、并发容器</span></p>

<h1><a name="t35"></a><a name="t35"></a>J.U.C之AQS</h1>

<p>AQS:AbstractQueuedSynchronizer</p>

<p>1 使用Node实现FIFO队列,可以用于构建锁或者其他同步装置的基础框架</p>

<p>2 利用了int类型表示状态</p>

<p>3 使用方法是继承</p>

<p>4 子类通过继承并通过实现它的方法管理器状态{acquire和release}的方法操纵状态</p>

<p>5 可以同时实现排它锁和共享锁模式(独占、共享)</p>

<h2><a name="t36"></a><a name="t36"></a>AQS同步组件</h2>

<p>1 CountDownLatch:闭锁,通过计数来保证线程是否需要一直阻塞</p>

<p>2 Semaphore:控制同一时间并发线程的数目</p>

<p>3 CyclicBarrier:和CountDownLatch相似,都能阻阻塞线程</p>

<p><span>4 ReentrantLock</span></p>

<p>5 Condition</p>

<p>6 FutureTask</p>

<h2><a name="t37"></a><a name="t37"></a>CountDownLatch</h2>

<p>CountDownLatch是一个同步辅助类,应用场景:并行运算,所有线程执行完毕才可执行</p>

<p><img alt=""height="432" src="https://img-blog.csdnimg.cn/20190116144228514.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="399"></p>

<p>代码示例1:</p>

<p><img alt=""height="366" src="https://img-blog.csdnimg.cn/2019011614542363.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="616"></p>

<p><img alt=""height="167" src="https://img-blog.csdnimg.cn/20190116145438494.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="488"></p>

<p>代码示例2:</p>

<p>await方法可以设定指定等待时间,超过这个时间久不再等待</p>

<p><img alt=""height="271" src="https://img-blog.csdnimg.cn/20190116150327429.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="483"></p>

<h2><a name="t38"></a><a name="t38"></a>Semaphore</h2>

<p>Semaphore可以很容易控制某个资源可悲同时访问的线程个数,和CountDownLatch使用有些类似,提供acquire和release两个方法,acquire是获取一个许可,如果没有就等待,release是在操作完成后释放许可出来。Semaphore维护了当前访问的线程的个数,提供同步机制来控制同时访问的个数,Semaphore可以实现有限大小的链表,重入锁(如ReentrantLock)也可以实现这个功能,但是实现上比较复杂。</p>

<p>Semaphore使用场景:适用于仅能提供有限资源,如数据库连接数</p>

<p>代码示例1:</p>

<p><img alt=""height="258" src="https://img-blog.csdnimg.cn/20190116151610407.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="399"></p>

<p><img alt=""height="117" src="https://img-blog.csdnimg.cn/20190116151624222.png" width="431"></p>

<p>代码示例2:</p>

<p><img alt=""height="162" src="https://img-blog.csdnimg.cn/20190116152040818.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="358"></p>

<p>&nbsp;</p>

<p><img alt=""height="141" src="https://img-blog.csdnimg.cn/20190116152151854.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="500"></p>

<h2><a name="t39"></a><a name="t39"></a>CyclicBarrier</h2>

<p>&nbsp;</p>

<p><img alt=""height="339" src="https://img-blog.csdnimg.cn/20190116152445148.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="333"></p>

<p>与CountDownLatch相似,都是通过计数器实现,当某个线程调用await方法,该线程就进入等待状态,且计数器进行加1操作,当计数器的值达到设置的初始值,进入await等待的线程会被唤醒,继续执行他们后续的操作。由于CyclicBarrier在释放等待线程后可以重用,所以又称循环屏障。使用场景和CountDownLatch相似,可用于并发运算。</p>

<p>CyclicBarrier和CountDownLatch区别:</p>

<p>1&nbsp;CountDownLatch计数器只能使用一次,CyclicBarrier的计数器可以使用reset方法重置循环使用</p>

<p>2&nbsp;CountDownLatch主要是视线1个或n个线程需要等待其他线程完成某项操作才能继续往下执行,CyclicBarrier主要是实现多个线程之间相互等待知道所有线程都满足了条件之后才能继续执行后续的操作,CyclicBarrier能处理更复杂的场景</p>

<p>代码示例:</p>

<p><img alt=""height="219" src="https://img-blog.csdnimg.cn/20190116162723477.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="461"></p>

<p><img alt=""height="89" src="https://img-blog.csdnimg.cn/20190116162737697.jpg" width="413"></p>

<h2><a name="t40"></a><a name="t40"></a>ReentrantLock</h2>

<p>reentrantLock(可重入锁)和synchronized区别</p>

<p>1 可重入性:同一线程可以重入获得相同的锁,计数器加1,释放锁计数器减1</p>

<p>synchronized也是可重入锁</p>

<p>2 锁的实现:synchronized依赖jvm实现(操作系统级别的实现),reentrantLock是jdk实现的(用户自己编程实现)</p>

<p>3 性能区别:synchronized在优化前性能比reentrantLock差,优化后性能有了恨到提升,相同条件下优先使用synchronized</p>

<p>4 功能区别:1)便利性方面,synchronized使用简单,reentrantLock需要手工加锁和释放锁2)锁的细粒度和灵活度方面,reentrantLock优于synchronized</p>

<p>5 reentrantlock独有的功能:1)可指定是公平锁还是非公平锁,synchronized只能是非公平锁 2)提供了一个Condition类,可以分组唤醒需要唤醒的线程 3)能够提供中断等待锁的线程机制,lock.lockInterruptibly()</p>

<p>代码示例:</p>

<p><img alt=""height="230" src="https://img-blog.csdnimg.cn/20190116170015736.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="441"></p>

<p><img alt=""height="174" src="https://img-blog.csdnimg.cn/20190116170028440.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="224"></p>

<h2><a name="t41"></a><a name="t41"></a>ReentrantReadWriteLock</h2>

<p>悲观写锁,即当所有读锁释放之后,才能加写锁,对于读多写少的程序,会引起堵塞或者死锁</p>

<p>代码示例:</p>

<p><img alt=""height="188" src="https://img-blog.csdnimg.cn/20190116172158307.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="518"></p>

<p><img alt=""height="210" src="https://img-blog.csdnimg.cn/20190116172216318.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="304"></p>

<h2><a name="t42"></a><a name="t42"></a>Condition</h2>

<p>多线程建协调通信的工具类</p>

<p>代码示例:</p>

<p><img alt=""height="262" src="https://img-blog.csdnimg.cn/20190116174215108.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="378"></p>

<p><img alt=""height="92" src="https://img-blog.csdnimg.cn/20190116174229662.jpg" width="288"></p>

<h2><a name="t43"></a><a name="t43"></a>FutureTask</h2>

<p>Callable与Runnable接口对比</p>

<p>Feature接口,可以得到任务的返回值</p>

<p>FeatureTask父类是RunnableFeature,RunnableFeature继承了Runnable和Feature两个接口</p>

<p>代码示例1:</p>

<p><img alt=""height="264" src="https://img-blog.csdnimg.cn/20190116192113526.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="475"></p>

<p>示例代码2:</p>

<p><img alt=""height="228" src="https://img-blog.csdnimg.cn/20190116192359825.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="579"></p>

<h2><a name="t44"></a><a name="t44"></a>Fork/Join框架</h2>

<p>将大人物切分成多个小任务并行执行,最后将结果汇总,思想和mapreduce类似。采用工作窃取算法,充分利用线程并行计算</p>

<h2><a name="t45"></a><a name="t45"></a>BlockingQueue——阻塞队列</h2>

<p>当队列满进行入队操作,线程阻塞,当队列空时进行出队操作,将会阻塞</p>

<p>线程安全,应用场景:生产者、消费者</p>

<h1><a name="t46"></a><a name="t46"></a>线程池</h1>

<p>new Thread弊端:</p>

<p>1 每次new Thread新建对象,性能差</p>

<p>2 线程缺乏统一的管理,肯无限制的新建线程,相互竞争,有可能占用过多系统资源导致死机或者OOM</p>

<p>3 缺少更多功能,如更多执行、定期执行、线程中断</p>

<p>线程池的好处:</p>

<p>1 重用存在的线程,减少对象创建、消亡的开销,性能佳</p>

<p>2 可有效控制最大并发的线程数,提高系统资源利用率,同时可以避免过多资源竞争,避免阻塞</p>

<p>3 提供定时执行、定期执行、单线程、并发数控制等功能</p>

<h2><a name="t47"></a><a name="t47"></a>线程池——ThreadPoolExecutor</h2>

<p>ThreadPoolExecutor参数:</p>

<p>1 corePoolSize:核心线程数</p>

<p>2 maximumPoolSize:最大线程数</p>

<p>3 workQueue:阻塞队列,存储等待执行的任务,很重要,会对线程池运行过程产生重大影响</p>

<p>如果当前系统运行的线程数量小于corePoolSize,直接新建线程执行处理任务,即使线程池中的其他线程是空闲的。如果当前系统运行的线程数量大于或等于corePoolSize,且小于maximumPoolSize,只有当workQueue满的时候才创建新的线程去处理任务,如果设置corePoolSize和maximumPoolSize相同的话,那么创建的线程池大小是固定的,这时如果有新任务提交,当workQueue没满时,把请求放进workQueue中,等待有空闲的线程从workQueue中取出任务去处理。如果运行的线程数量大于maximumPoolSize,这时如果workQueue满,根据拒绝策略去处理。</p>

<p>4 keepAliveTime:线程没有任务执行时最多保持多久的时间终止</p>

<p>5 unit:keepAliveTime的时间单位</p>

<p>6 threadFactory:线程工厂,用来创建线程</p>

<p>7 rejectHandler:当拒绝处理任务时的策略</p>

<p>线程池状态:</p>

<p><img alt=""height="338" src="https://img-blog.csdnimg.cn/20190116200916499.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="678"></p>

<p>线程池方法:</p>

<p><img alt=""height="147" src="https://img-blog.csdnimg.cn/20190116201034788.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="414"></p>

<p><img alt=""height="132" src="https://img-blog.csdnimg.cn/20190116201155474.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="379"></p>

<p><img alt=""height="208" src="https://img-blog.csdnimg.cn/2019011620154488.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="315"></p>

<p><img alt=""height="174" src="https://img-blog.csdnimg.cn/20190116202302976.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="368"></p>

<p><img alt=""height="423" src="https://img-blog.csdnimg.cn/20190116202516772.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="862"></p>

<h1><a name="t48"></a><a name="t48"></a>死锁</h1>

<p>线程(进程)互相等待对方释放资源产生死锁</p>

<p><img alt=""height="228" src="https://img-blog.csdnimg.cn/2019011709443729.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="263"></p>

<p>&nbsp;</p>

<p><img alt=""height="236" src="https://img-blog.csdnimg.cn/20190117095644178.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="405"></p>

<p><img alt=""height="155" src="https://img-blog.csdnimg.cn/2019011709583237.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="320"></p>

<p><img alt=""height="66" src="https://img-blog.csdnimg.cn/20190117095955851.png" width="298"></p>

<h1><a name="t49"></a><a name="t49"></a>HashMap与ConcurrentHashMap</h1>

<p>HashMap在多线程环境中做rehash时容易产生死循环</p>

<p><img alt=""height="263" src="https://img-blog.csdnimg.cn/2019011710154637.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="346"></p>

<p><img alt=""height="281" src="https://img-blog.csdnimg.cn/20190117101614676.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="441"></p>

<p><img alt=""height="311" src="https://img-blog.csdnimg.cn/20190117101329888.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="399"></p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<p><img alt=""height="455" src="https://img-blog.csdnimg.cn/2019011710183965.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="726"></p>

<p><img alt=""height="312" src="https://img-blog.csdnimg.cn/2019011710320472.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="617"></p>

<p><img alt=""height="218" src="https://img-blog.csdnimg.cn/20190117110651209.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="457"></p>

<p><img alt=""height="192" src="https://img-blog.csdnimg.cn/20190117111204911.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="317"></p>

<p><img alt=""height="168" src="https://img-blog.csdnimg.cn/20190117111403507.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="407"></p>

<p><img alt=""height="299" src="https://img-blog.csdnimg.cn/20190117111608183.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="354"></p>

<p>Guava Cache是谷歌开源的java工具库,借鉴ConcurrentHashMap的设计思想</p>

<p><img alt=""height="246" src="https://img-blog.csdnimg.cn/20190117113310532.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="419"></p>

<p><img alt=""height="277" src="https://img-blog.csdnimg.cn/20190117113715812.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="407"></p>

<p><img alt=""height="232" src="https://img-blog.csdnimg.cn/20190117140838502.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="358"></p>

<p><img alt=""height="253" src="https://img-blog.csdnimg.cn/20190117141015361.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="472"></p>

<p><img alt=""height="372" src="https://img-blog.csdnimg.cn/20190117141326883.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="345"></p>

<p>&nbsp;</p>

<h1><a name="t50"></a><a name="t50"></a>消息队列</h1>

<p>异步解耦</p>

<p>Kafka:持久化、高吞吐、分布式</p>

<p><img alt=""height="190" src="https://img-blog.csdnimg.cn/20190117184555275.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="257"></p>

<p><img alt=""height="84" src="https://img-blog.csdnimg.cn/20190117184605694.png" width="311"></p>

<p><img alt=""height="196" src="https://img-blog.csdnimg.cn/201901171846136.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="258"></p>

<p>&nbsp;</p>

<h1><a name="t51"></a><a name="t51"></a>应用拆分</h1>

<p>拆分原则:</p>

<p><img alt=""height="221" src="https://img-blog.csdnimg.cn/20190117184627590.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="269"></p>

<p><img alt=""height="190" src="https://img-blog.csdnimg.cn/20190117184635695.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="360"></p>

<p><img alt=""height="251" src="https://img-blog.csdnimg.cn/20190117184747753.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="400"></p>

<p>&nbsp;</p>

<h1><a name="t52"></a><a name="t52"></a>服务降级和熔断</h1>

<p><img alt=""height="197" src="https://img-blog.csdnimg.cn/20190117184754244.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="431"></p>

<p>&nbsp;</p>

<p><img alt=""height="384" src="https://img-blog.csdnimg.cn/20190117184808127.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="491"></p>

<p><img alt=""height="523" src="https://img-blog.csdnimg.cn/20190117184818816.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0dhdmluWmhlcmE=,size_16,color_FFFFFF,t_70" width="728"></p> </div>

<p>另外安利一个学习教程:<a href="http://www.itcsdr.com/blog/24/"><strong>(完整)Java并发编程与高并发解决方案</strong></a></p> <p>分享一个it资源平台:<a href="http://www.itcsdr.com"><strong>点击进入</strong></a></p>

今天关于微信小程序商城高并发解决方案小程序如何解决高并发的介绍到此结束,谢谢您的阅读,有关java 高并发解决方案、java并发编程与高并发解决方案、Java并发编程与高并发解决方案(一)-并发与高并发基本概念、java高并发解决方案等更多相关知识的信息可以在本站进行查询。

本文标签: