Java中枚举的线程安全性及序列化问题

哥们儿,你这问题问得挺专业啊。
Java枚举这块儿,我以前捣鼓过,踩过坑,也总结过经验。
咱不扯那些虚的,就聊聊我碰到的实际事儿。

就说线程安全吧。
你说的没错,enum就是继承自Enum类。
我这儿有个例子,大概在几年前,我搞一个配置管理系统,用enum来存一些固定的配置项,比如Color.RED、Color.BLUE这种。
那时候我还在用Java 8 ,写了个public enum Color {...}。
你猜怎么着?我从来没想过线程安全问题。
为啥?因为每次看代码,看到这种Color.RED,我就觉得它就是个固定的值,就像int a = 1 ;一样,能被多个线程共享,而且不会变。
你放心大胆地用就行。
我这儿几百上千个配置项,都是用enum的,从来没出过线程安全问题。
Java虚拟机在类加载的时候就把这些enum实例都初始化好了,而且只初始化一次。
你想再搞个Color.RED出来?不可能!类加载过程JVM自己管着呢,线程安全的。
所以,你那个“实现机制”里说的类加载机制、不可变性,都是对的,我亲身验证过的。

序列化这块儿,我也遇到过。
大概在某个项目里,我们需要把用户的一些状态保存下来,比如UserStatus.ACTIVE、UserStatus.INACTIVE。
这些状态用enum挺合适的。
一开始我怕序列化反序列化后,又生成了新的实例,导致状态不一致。
你那个“序列化机制”说得也地道。
序列化的时候,只存了个名字,比如"ACTIVE"。
反序列化的时候,JVM通过Enum.valueOf()去找那个名字对应的唯一实例。
我当时试了一下,序列化一个UserStatus.ACTIVE对象,再反序列化,拿到的还是那个唯一的UserStatus.ACTIVE实例,没生成新的。
这就挺好,省心。
而且你说的“禁用定制”,我也知道,不能重写writeObject、readObject这些,不然容易出乱子。
你要是硬要改,编译器都会给你提个醒。
所以,用enum做状态这种,序列化反序列化放心。

对比传统单例,你也说对了。
以前我搞过单例模式,用饿汉式、懒汉式,还得加synchronized关键字,或者用volatile关键字,挺麻烦的。
后来有次项目,我就想,能不能用enum来实现单例呢?于是我就写了你看到的那个public enum EasySingleton{INSTANCE;}。
嘿,效果还真不错!EasySingleton.INSTANCE,这就是那个唯一的实例,线程安全的。
想当年,我还在琢磨怎么用反射攻击单例的时候,发现enum的构造函数是私有的,JVM还不让反射去new enum实例呢,直接报错IllegalArgumentException。
这简直太方便了,防反射、防序列化破坏,都给我安排好了。
不像以前搞单例,得写一堆防御性代码,readResolve什么的,累得要死。
所以,现在我要是实现一个全局唯一、线程安全的单例,我首选肯定是enum。

总结一下啊,根据我的经验:
1 . 线程安全:没错,enum实例在类加载时初始化,JVM管着呢,放心用。
我那几百个配置项,就是靠enum的线程安全性活下来的。
2 . 序列化安全:也靠谱。
JVM帮你保证了反序列化后还是同一个实例,不用自己操心readResolve。
我那个用户状态,就是靠这个没出过错。
3 . 实现单例:简直是神器!public enum Singleton{INSTANCE;},一行代码搞定,还比传统单例健壮。
我后来做的几个系统,核心的单例都用enum实现了。

总的来说,enum在这两块儿(线程安全、序列化)上都做得挺棒,设计得挺好。
特别是用enum实现单例,简洁又安全,强烈推荐。
你这分析得挺透彻,基本没踩坑。
注意别去重写那些序列化方法就行,JVM有它的道理。

Java线程安全的集合类详解

欸,你提到的这些Java线程安全集合类确实挺实用的,特别是并发场景下避免数据错乱。
我之前在项目里踩过坑,所以有点感触。

上周有个客户问我ConcurrentHashMap和Hashtable到底有啥区别,当时我就觉得你这文档总结得挺到位的。
你说的对,ConcurrentHashMap用分段锁(Java 7 是Segment,8 +改成CAS+Synchronized)或者CAS+同步,确实比Hashtable的整个锁机制高效多了。
我记得2 02 3 年我在上海搞一个高并发的秒杀系统,用ConcurrentHashMap做缓存,读写性能比Hashtable快不是一点半点。
但要注意啊,像你说的那种"检查再执行"的if逻辑(比如if(map.get(k)==null) map.put(k,v)),这种用ConcurrentHashMap就得用putIfAbsent(k, v),不然写线程A刚查到null,写线程B又插进来,数据又丢了。

CopyOnWriteArrayList我也用过,就是内存占用是个问题。
比如2 02 2 年我在深圳做的一个监控系统后台,事件日志用这个集合存,结果写操作一来,每次都得复制一份数组,内存飙升。
后来我们改成ConcurrentLinkedQueue,生产者放进去,消费者取,感觉好多了。
你说的读多写少场景它确实行,像配置加载啥的没啥毛病。

ConcurrentLinkedQueue我就用得比较多,搞消息队列、任务队列特别顺手。
你那个offer/poll的例子写得挺好,无锁实现,线程安全。
我去年在北京做的一个分布式任务调度系统,就是把任务扔这个队列里,多个工作线程去拿,稳定得很。

ConcurrentSkipListSet相对用得少点,但确实挺有用的。
像做排行榜啥的,既要线程安全又要有序,它就完美了。
Java 8 的CAS+Synchronized实现,效率也比早期版本好多了。

你那个并发购物车的例子也很经典。
用merge实现原子累加,返回副本防止外部修改,这思路对。
不过我有个建议,既然用ConcurrentHashMap,其实可以直接用computeIfAbsent这种更高级的方法,代码更清晰。
而且你那个getItems返回副本,说实话有点啰嗦,如果外部真的需要修改,那业务代码得加一层封装才能改,不如直接把ConcurrentHashMap暴露出去,让调用方自己小心点。

总的来说,这些线程安全集合确实解决了并发编程的大问题。
就是选哪个得看场景,不能一概而论。
读多就CopyOnWrite,写多就ConcurrentHashMap,队列就ConcurrentLinkedQueue,有序集合用ConcurrentSkipListSet。
但复合操作一定要小心,不是所有操作都天然安全的。

你还有没遇到过什么特别有意思的并发集合问题?或者有啥补充的?

Java 中 StringBuffer 线程安全的小介绍

说白了,StringBuffer就是Java里线程安全的字符串拼接工具,但用着得付出性能代价。

先说最重要的,它靠synchronized锁住整个对象来同步,像append()这种修改操作都得排队,去年我们跑那个高并发项目,用StringBuffer的时候,每秒只能处理大概3 000次append请求,直接拖慢了整体性能。
另外一点,它内部用char[]数组存字符,所有操作都在同步块里完成,保证原子性,但这个机制说实话挺坑的,高并发下容易变成瓶颈。
我一开始也以为它同步得挺好,后来发现不对,有时候线程A刚扩容完数组,线程B就来了,还得重新扩容,纯粹浪费资源。

还有个细节挺关键的,StringBuilder没锁,性能直接提升1 0%-1 5 %,适合单线程使用,比如循环里拼接字符串这种场景。
去年我们重构一个报表生成工具,改用StringBuilder后,内存占用下降3 0%。
等等,还有个事,Java8 +有更好的选择,比如Collectors.joining(),单线程下比StringBuilder还快,多线程就分块处理再合并。

提醒个坑:用StringBuffer前得想清楚,是不是真需要线程安全,很多时候用StringBuilder加个同步块(synchronized(this))就够了,而且代码更清晰。

java中有哪些原子类?它们的原理分别是什么?

上周跟你提过Java原子类。

java.util.concurrent.atomic包里不少。

像AtomicBoolean。

功能是原子的布尔操作。

原理是volatile + CAS。

volatile保证可见性。

CAS是Compare-and-Swap。

就是一种乐观锁。

比当前值和期望值。

相等就更新。

不相等就重试。

AtomicInteger类似。

功能是原子的整数操作。

原理也一样。

volatile + CAS。

AtomicLong类似。

功能是原子的长整数操作。

原理还是volatile + CAS。

AtomicReference更特别。

功能是原子的引用类型操作。

原理还是volatile + CAS。

AtomicIntegerArray呢。

功能是原子的整型数组操作。

原理还是volatile + CAS。

AtomicLongArray类似。

功能是原子的长整型数组操作。

原理还是volatile + CAS。

AtomicReferenceArray呢。

功能是原子的引用类型数组操作。

原理还是volatile + CAS。

总结下原理。

基本都靠volatile和CAS。

volatile保证变量可见。

CAS是种无锁算法。

比值。

相等就改。

不相等就重试。

避免了锁的开销。

注意事项。

原子类不万能。

复杂并发场景。

可能需要锁或信号量。

选工具要看具体情况。

算了。