78.  同步访问共享的可变数据

  为了在线程之间进行可靠的通信,也为了互斥访问,同步是必要的。

  不共享可变的数据。要么共享不可变的数据,要么压根不共享。换句话说,将可变数据限制在单线程中。

  当多个线程共享可变数据的时候,每个读或写数据的线程必须执行同步。如果没有同步就无法保证一个线程所做的修改被另一个线程获知。如果只需要线程间的交互通信而不需要互斥,volatile是一种可以接受的同步形式。

   这个在工作中也需要注意,在学习完多线程编程之后更好理解。

79.  避免过度同步

  过度同步可能会导致死锁、降低性能,甚至不确定的行为。(切记不能循环锁对象造成死锁)

  在同步区域内应该做很少的事情。获得锁、检查共享数据、转换数据、释放锁。如果执行很耗时的操作,应该放在同步之外。也就是说同步块中应该做尽可能少的事情。

80.  Executor、task和stream优先于线程

  如果需要用到多线程处理一些耗时的操作,建议使用现有的Executor。不能编写自己的工作队列、而且还应该尽量不使用线程Thread。

  

阿里规约对多线程的使用有如下建议:

(1)创建线程或线程池时请指定有意义的线程名称,便于排错(线程池需要指定线程工厂)

(2)线程资源必须通过线程池创建,不能在应用程序中显示创建

(3)线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

说明: Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致 OOM

81.  并发工具优于wait和notitfy

  正确地使用wait和notify比较困难,应该使用比较高级的并发工具来代替。比如:Executor Framework(ExecetorService)、并发集合以及同步器Synchronizer(屏障、闭锁)。

  并发集合中不可能排除并发获得,将它锁定没什么用,只会使程序的性能变慢。尽量使用并发容器比如ConcurrentHashMap,而不是使用Collections.synchronizedMap。

  同步器是使线程能够等待另一个线程的对象,允许它们协调工作。最常见的同步容器是CountDownLatch和Semaphore。还有CyclicBarrier和Exchang。

  总而言之,直接使用wait和notify就像使用"并发汇编语言"编程一样,而java.util.concurrent包则提供了更高级的语言。没有理由在新代码中使用wait方法和notify方法,即使有也是极少数的。如果你在维护wait、notify的代码,务必确保内部是利用标准的模式从while循环内部调用wait方法。一般情况下,应该优先使用notifyAll方法,而不是使用notify方法。如果使用notify方法,请一定要小心确保程序的灵活性。

82.  文档应包含线程安全属性

  要启用安全的并发使用,类必须清楚地记录它支持的线程安全级别。如下:

(1)不可变的:这个类的实例看起来是常量,不需要同步,比如String、Long、BigInteger

(2)无条件线程安全:该类内部有足够的线程安全,比如AtomicLong和ConcurrentHashMap等。

(3)有条件的线程安全:比如Collections.synchronized包装器返回的集合,其迭代器需要外部同步。

(4)非线程安全:该类的实例是可变的,要并发地使用它们,客户端需要同步,比如ArrayList、HashMap

(5)线程对立: 即使每个方法调用都被外部同步包围,该类对于并发使用也是不安全的。线程对立通常是由于在不同步的情况下修改静态数据而导致的。没有人故意编写线程对立类;此类通常是由于没有考虑并发性而导致的。当发现类或方法与线程不相容时,通常将其修复或弃用。

  注意:lock字段必须声明为final,防止被修改引用。可以使用一个内部私有的对象锁进行lock。

83.  明智审慎的使用延迟初始化

  延迟初始化是延迟字段的初始化,直到需要它的值。这种技术适用于静态字段也适用于实例字段。

  在存在多个线程的情况下,使用延迟初始化很棘手。如果两个或多个线程共享一个延迟初始化的字段,那么必须使用某种形式的同步,否则会导致严重的错误

  在多数情况下,常规初始化优于延迟初始化。

  如果需要使用延迟初始化来提高实例字段的性能,请使用双重检查模式。这个模式避免了初始化后访问字段时的锁定成本。这个模式背后的思想是两次检查字段的值(因此得名 double check):一次没有锁定,然后,如果字段没有初始化,第二次使用锁定。只有当第二次检查指示字段未初始化时,调用才初始字段。由于初始化字段后没有锁定,因此将字段声明为 volatile 非常重要:

    // Double-check idiom for lazy initialization of instance fields
private volatile FieldType field; private FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized (this) {
if (field == null) // Second check (with locking)
field = result = computeFieldValue();
}
}
return result;
}

84.   不要依赖线程调度器

  任何依赖线程调度器来保证正确性或性能的程序都可能是不可移植的。

  编写健壮、响应快、可移植程序的最佳方法是确保可运行线程的平均数量不显著大于处理器的数量。这使得线程调度器几乎没有选择:它只运行可运行线程,直到它们不再可运行为止。即使在完全不同的线程调度策略下,程序的行为也没有太大的变化。注意,可运行线程的数量与线程总数不相同,后者可能更高。正在等待的线程不可运行。
  保持可运行线程数量低的主要技术是让每个线程做一些有用的工作,然后等待更多的工作。如果线程没有做有用的工作,它们就不应该运行。 对于 Executor 框架,这意味着适当调整线程池的大小,并保持任务短小(但不要太短),否则分派的开销依然会损害性能。

  总之,不要依赖线程调度器来判断程序的正确性。生成的程序既不健壮也不可移植。因此,不要依赖Thread.yield 或线程优先级。这些工具只是对调度器的提示。线程优先级可以少量地用于提高已经工作的程序的服务质量,但绝不应该用于「修复」几乎不能工作的程序。

85.  优先选择Java序列化的替代方法

  序列化是危险的,应该避免。如果可以,用JSON(文本)或者Protobuf(二进制)代替,永远不要反序列化不可信的东西。如果必须这么做,请使用对象反序列化过滤。

  

86.  非常谨慎的实现Serializable

  实现该接口的一个重要代价就是,一旦类的实现被发布,它就会降低更改该类实现的灵活性。

  第二个代价是增加了出现bug和安全漏洞的可能性。

  第三个代价是,它增加了与发布类的新版本相关的测试负担。

87.  考虑自定义的序列化形式

  无论选择哪种序列化形式,都要在编写的每个可序列化类中声明显式的序列版本 UID。

    private static final long serialVersionUID = randomLongValue;

88. 保护性的编写 readObject 方法

89. 对于实例控制,枚举类型优于 readResolve

90. 考虑用序列化代理代替序列化实例

  序列化代理模式相当简单。首先,为可序列化的类设计一个私有的静态嵌套类,精确地表示外围类的逻辑状态。这个嵌套类被称为序列化代理(seralization proxy),它应该有一个单独的构造器,其参数类型就是那个外围类。这个构造器只是从它的参数中复制数据:它不需要进行任何一致性检验或者保护性拷贝。从设计的角度看,序列化代理的默认序列化形式是外围类最好的序列化形式。外围类及其序列代理都必须声明实现 Serializable 接口。

Effective.Java第78-90条(同步相关)的更多相关文章

  1. <<Effective Java>> 第四十三条

    <<Effective Java>> 第四十三条:返回零长度的数组或者集合,而不是null 如果一个方法的返回值类型是集合或者数组 ,如果在方法内部需要返回的集合或者数组是零长 ...

  2. Effective java 系列之避免过度同步和不要使用原生态类型,优先考虑泛型

    避免过度同步(67):在一个被同步的方法或代码块中,不要调用哪些被设计成被覆盖的方法或者是由客户端以函数对象的形式提供的方法(21). 有点拗口,书上提供的创建者与观察者模式,add方法太多,看得眼花 ...

  3. Effective java读书札记第一条之 考虑用静态工厂方法取代构造器

    对于类而言,为了让client获取它自身的一个实例,最经常使用的方法就是提供一个共同拥有的构造器. 另一种放你发,也应该子每一个程序猿的工具箱中占有一席之地.类能够提供一个共同拥有的静态 工厂方法.它 ...

  4. <<Effective Java>>之善用组合而不是继承

    使用JAVA这门OO语言,第一要义就是,如果类不是专门设计来用于被继承的就尽量不要使用继承而应该使用组合 从上图2看,我们的类B复写了类A的add喝addALL方法,目的是每次调用的时候,我们就能统计 ...

  5. Java异常(二) 《Effective Java》中关于异常处理的几条建议

    概要 本章是从<Effective Java>摘录整理出来的关于异常处理的几条建议.内容包括:第1条: 只针对不正常的情况才使用异常第2条: 对于可恢复的条件使用被检查的异常,对于程序错误 ...

  6. 阅读《Effective Java》每条tips的理解和总结(1)

    <Effective Java>这本书的结构是90来条tips,有长有短,每条tip都值的学习.这里根据对书中每条tip的理解做简短的总结,方便日后回顾.持续更新~ 1. 考虑用静态方法代 ...

  7. Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法

    避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...

  8. [Effective Java]第八章 通用程序设计

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  9. Effective Java通俗理解(下)

    Effective Java通俗理解(上) 第31条:用实例域代替序数 枚举类型有一个ordinal方法,它范围该常量的序数从0开始,不建议使用这个方法,因为这不能很好地对枚举进行维护,正确应该是利用 ...

随机推荐

  1. 微信小程序环境配置和开发!!

    1.登陆微信公众平台小程序,下载 普通小程序开发者工具.或者 小游戏开发者工具. 2.新建项目需要填以下几点,然后初始demo如下,注意rpx是分成750份的单位. 3.点击预览,用微信扫描二维码,代 ...

  2. android studio学习---菜单栏BUILD功能

    项目构建: 1.构建项目 2.重构项目 3.签名打包

  3. 人大金仓KCI

    #include "bin/libkci.h" static void exit_nicely(KCIConnection *conn) { KCIConnectionDestor ...

  4. sqlplus连接远程Oralce数据库

    1. 下载 http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html 2. 三个包 in ...

  5. 如何修改被readonly修饰的属性

    结论: 1.用KVC改变只读属性的值: 2.若禁止KVC方式修改只读属性的值,可在对应类重写类方法 // 该方法默认返回YES. 即在不存在满足条件的存取方法时,允许直接访问属性对应的实例变量+ (B ...

  6. Spark GraphX图计算简单案例【代码实现,源码分析】

    一.简介 参考:https://www.cnblogs.com/yszd/p/10186556.html 二.代码实现 package big.data.analyse.graphx import o ...

  7. C++ OpenSSL 之二:生成RSA文件

    1.等同于生成private key: openssl genrsa -out "save_path" 2048 2.代码如下 bool MakeRsaKeySSL(const c ...

  8. cookie跨域解决方案

    cookie的名/值对中的值不允许出现分号.逗号和空白符,因此在设置cookie前要用encodeURIComponent()编码,读取时再用decodeURIComponent()解码. cooki ...

  9. linux的性能调优

    单机调优: 分析性能瓶颈的原因,解决它.   cpu子系统 内存子系统 IO子系统 网络系统       @cpu子系统调优 cpu技术指标 xeon E5520 2.27GHz 8192kb # c ...

  10. jQuery的Promise 这里介绍的很详细

    原文电梯:https://www.cnblogs.com/yelongsan/p/7644239.html 先前了解了ES6的Promise对象,来看看jQuery中的Promise,也就是jQuer ...