怎样实现线程安全 实现线程安全的四种方式

说实话,当我第一次开始一个多线程项目时,谈论续集让我非常头疼。
有趣的是,在这四种主要方法中,我拥有的第一个是“避免公共变量的状态”——当时我手里有一个电商系统,库存数据是通过多线程组织直接抓取的。
因此,信息就像一个幽灵旅行者。
后来我改变了主意,为每个线程创建了一个单独的库存实例。
虽然解决了碰撞问题,但是内存使用量却增加了一倍。
奴隶主差点把我气死。

说到不可变的事物,我看到了一个很好的例子。
当某个支付系统处理退款请求时,会立即关闭带有final关键字的交易记录对象。
任何字符串只能读取而不能修改。
该代码已经运行了两三年,并且从未发生过任何在线数据泄露事件。
但说实话,使用不可变对象的前提是对象能够接收实时性较差的信息。
例如,当用户检查其帐户余额时,他们不能每次都向网络发送请求以检查正确的数据库吗?
虽然我被锁定机制所困,但我首先记住了重建订单模块流程的经历。
以前,使用synchronized 关键字时很不幸会发生死锁。
后来换成了ReentrantLock和Condition,终于可以可视化等待队列的线程了。
但我记得锁粒度从方法级降低到网格级后,碰撞量直接翻倍。
具体数字可能会在当年的报告中确定。

我很习惯使用原子变量,而不是场景。
使用 AtomicInteger 进行同步存储 (i++) 感觉就像从拎包换成了电动叉车。
虽然是CAS底层,但是确实是可以自由编写的。
但需要注意的是,原子变量仅存储在堆内存中,必须使用其他手段来更新对象指针。
最后我们谈论安全收藏。
我使用 ConcurrentHashMap 已有十多年了。
最烦人的就是扩容时的短期写阻塞,不过这比HashTable线程多了就挂掉的状态要好很多。
我认为 CopyOnWriteArrayList 是“好但没用”的东西。
有一次尝试实现的时候,发现改变写入操作的顺序成本太高,所以最后改成了实现分发。

这些方法中确实没有绝对最优的方法,我们只说锁。
头发使用不当比没有头发更可悲。
我见过一些团队未能包含锁的粒度,最终系统达到了 CPU 线程池 1 00% 满的程度。
另一方面,原子变量虽然简单,但可以很容易地在复杂的场景中使用。
说白了,健康线就像适应环境一样。
应经常根据实际负载情况进行试验,找到最佳点。

线程池使用及优势

关于池塘继承的讨论是一个古老的话题。
我们还得从2 007 年说起,当时Java刚刚推出了Executor框架,那时候的线程池还没有现在这么复杂。
主要任务是控制线程数量,设置任务,然后启动。
就像工厂一样,你把工人当作零件,把电线当作工人,把工厂当作电线槽。

当时我不明白为什么电线会被退回?我想了想,那些工人完成一项任务后并不会下班,而是可以继续进行下一项任务。
而这种方式,人多用,效率自然就提高了。

谈控制最大碰撞次数,就像工厂老板规定最多可以同时容纳1 00名工人一样。
如果有 1 00 个工作岗位到达,只有前 1 00 个工人可以先做,后面的必须排队等候。

管理织机就像厂长一样,负责安排工人(织机)的工作,使工作能够顺利进行。
但主要有三个优点。
第一,是否可以省财。
不需要为每个任务创建一个新的线程,线程都会被返回。
其次,控制碰撞次数,避免系统崩溃。
第三,简化编程,使用可执行框架更轻松地管理线程。

Java中的线程池是由执行器框架填充的。
Executors、Executors、ExecutorService 和 Thread PoolExecutor 都是这项工作的一部分。
执行者就像我住的工厂,用来制造续作坦克。

常用的有几个线程池,比如FixedThreadPool、CachedThreadPool、SingleThread Executor、ScheduledThreadPool。
如果仔细看这三个工厂方法的源码,你会发现所有的ThreadPoolExecutor对象都是在底层创建的。

这个ThreadPoolExecutor的构造方法有7 个参数,相当复杂。
但开发者很简单,就是当队列满了,线程数达到最大值时,新的任务就会被拒绝,直到有空间或者空闲线程为止。

拒绝策略有四种类型,全部针对业务。
所有这些计划都实现了Rejected ExecutionHandler工具,该工具相当专业。

说实话,如果不进行非常深入的调查,想要了解这件事确实不容易。
然而,理解对于提高系统性能非常有用。

线程池的四种创建方式及区别

结论:当线程池管理任务执行时,需要注意线程计数和排队策略。

1 .核心线程数为0,非核心线程已满,任务继续创建非核心线程。
2 .FixedThreadPool是固定长度的线程池。
当任务较多时,队列满了,资源很容易溢出。
3 .单线程SingleThreadPool,顺序执行,无并发问题。
4 . ScheduledThreadPool支持定时任务、固定核心数、不完整核心、周期任务。

要点:
fixedThreadPool:核心数=非核心数,队列无界,资源有限,容易溢出。

SingleThreadPool:核心数=零核心数=1 ,无限队列,顺序执行。

ScheduledThreadPool:固定核心数,核心未满,支持定时重复任务。

拒绝政策:
DefaultAbortPolicy:抛出异常并不礼貌地处理它。

CallerRunsPolicy:任务返回到主线程,影响主线程的任务。

DiscardPolicy/Oldest:丢弃任务,适合不重要的任务。

自定义拒绝策略:实现RejectedExecutionHandler接口。

你自己考虑一下。