Effective Java笔记(五)
并发
78、同步访问共享的可变数据
- 同不仅可以阻止一个线程看到对象处于不一致的状态之中,它还保证进入同步方法或者同步代码块的每个线程,都能看到由同一个锁保护的之前所有的修改效果;
- 在读或者写原子数据的时候,不会看到任意的数组,但是它也不保证一个线程写入的值,对于另外一个线程将是可见的,为了在线程键进行可靠的通信,也为了互斥访问,同步是有必要的
- 千万不要使用Thread.stop方法;要阻止一个线程(A)妨碍另一个线程(B),可以设置一个静态boolean域(boolean原子性,不需要同步),B将A中的域设置为true,表示A线程将终止自己,A轮询查看此变量的值;
- volatile修饰符不执行互斥访问,但它保证任何一个线程在读取该值的时候都将看到最近刚刚被写入的值;
- 当多个线程共享可变数据的时候,每个读或写数据的线程都必须执行同步;未能同步共享可变数据会造成程序的活性失败;
79、避免过度同步
- 为了避免活性失败和安全性失败,在一个被同步的方法或者代码块中,永远不要放弃对客户端的控制;换句话说,就是一个被同步的区域内部,不要调用设计成要被覆盖的方法,或者由客户端以函数对象的形式提供的方法;
- CopyOnWriteArrayList通过重新拷贝整个底层数组*,在这里实现所有的写操作,由于内部数组永远不改动,迭代不需要锁定,速度非常快*。适于用改动小,却需要经常遍历的集合
- 通常来说,应该在同步区域尽量做可能少的工作;
80、executor、task和stream 优先于线程
- exex.execute(runnable);执行提交一个runnable方法;
- exec.shutdown(); 优雅终止
- invokeAny或invokeAll 等待任务集合的任何任务或所有任务完成;
- awaitTermination 等待 优雅地完成终止
- ExecutorCompletionService 任务完成时逐个获取这些任务的结果
81、并发工具优先于wait和notify
- 并发集合中不可能排除并发活动,将它锁定没有什么作用,只会使程序的速度变量;
- 应该优先使用ConcurrentHashMap,而不是使用Collections.synchronizedMap;
- 有些集合接口已经通过阻塞操作扩展。例如BlockingQueue
- 同步器是使线程能够等待另一个的对象,允许它们协调动作。(CountDownLatch、Semaphore、CyclicBarrier、Exchanger)
- CountDownLatch(倒计数锁存器)
- 对于间接式的定时,始终应该优先使用Systen,nanoTime,而不是使用System.currentTimeMills;
- 没有理由在新代码中使用wait方法和notify方法,即使有,也是极少的;
82、线程安全性的文档化
- lock域应该始终声明为final
83、慎用延迟初始化
序列化
对象序列化:用来将对象编码成字节流(序列化),并从字节流编码中重新构建对象(反序列化)。一旦对象被序列化后,它的编码就可以从一台正在运行的虚拟机传递到另一台虚拟机上,或者被存储到磁盘上,供后续反序列化时使用;
85、其他方法优先于Java序列化
- 避免序列化攻击的最佳方式是永远不要反序列化任何东西;
86、谨慎地实现Serializable接口
87、考虑使用自定义的序列化形式
88、保护性地编写readObject方法
89、对于实例控制、枚举类型优先于readResolve
90、考虑用序列化代理代替序列化实例
参考书籍《Effective Java》