并发

66,同步访问共享的可变数据

关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块。

同步不仅可以阻止一个线程看到对象处于不一致的状态中,它还可以保证进入同步方法或者同步代码块的每个线程,都看到同一个锁保护的之前的修改效果。

Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。对变量的写操作不依赖于当前值,该变量没有包含在具有其他变量的不变式中。不具有原子性

将可变数据控制在单个线程中,不共享可变数据。当多个线程共享可变数据的时候,每个读或写数据的线程都必须执行同步。

但一个线程短时间内修改一个数据对象,然后与其他线程共享,这是尅接受的,只同步共享对象引用的动作。其他线程只是读取没有修改,这种对象事实上是不可变的,将这种对象从一个线程传递到其他线程被称作安全发布

安全发布对象方法:可以将它保存在静态域中,作为类初始化的一部分;可以将它保存在volatile域,final域或者通过正常锁定访问的域中;或者将它放到并发集合中。

67,避免过度同步

依据情况不同,过度同步可能会导致性能降低,死锁,甚至不确定的行为。

为了避免活性失败和安全性失败,在一个被同步的方法或代码块中,永远不要放弃对客户端的控制。换句话说,在一个被同步的区域内部,不要调用设计成要被覆盖的方法,或者由客户端以函数对象的形式提供的方法。

在同步区域调用外来方法被称作“开放调用”。除了可以避免死锁之外,开放调用还可以极大地增加并发性。

通常,你应该在同步区域内做尽量少的工作。

在这个多核的时代多度同步的实际成本并不是指获得锁所花费的CPu时间;而是指失去了并行地机会,以及因为需要确保每个核都有一个一致的内存视图而导致的延迟。过度同步的另一项潜在开销在于,他会限制Vm优化代码的能力。

StringBuffer实例几乎总是被用于单个线程中,而它们执行的却是内部同步。为此,StringBuffer基本都由StringBuilder代替。

如果一个可变类要并发使用,应该使这个类编程线程安全的,通过内部同步,你还可以获得,明显比外部锁定整个对象更高的并发性。否则,就不要在内部同步。让客户在必要的时候从外部同步。

68,executor和task优先于线程
  • 如果编写的是小程序,或者轻载的服务器,使用Executor.newCachedThreadPool通常是个不错的选择。
  • 在大负载的服务器中,最好使用Executor.newFixedThreadPool,它为你提供了一个包含固定线程数目的线程池,或者为了最大限度的控制它,就直接使用ThreadPoolExecutor类。

你不仅应该尽量不要编写自己的工作队列,而且还应该尽量不直接使用线程。现在的关键抽象不再是Thread了,它以前既充当工作单位,又是执行机制。工作单位和工作单位是分开的,现在的关键抽象是工作单元,称作任务task)。

任务有两种:Runnable及其近亲Callable(它与Runnable类似,但它会返回值)。执行任务的通用机制是executor service。

Executor FrameWork也有一个可以代替java.util.Timer的东西,即ScheduledThreadPoolExecutor。

Timer只有一个线程来执行任务,如果timer唯一的线程抛出未被捕捉的异常,timer就会停止工作。而线程池executor支持多个线程,并且优雅的从抛出未受检异常的任务中恢复。

69,并发工具优先于wait和notify

java.util.concurrent中更高级的工具分三类:Executor Framework,并发集合(Concurrent Collection)以及同步器(Synchronizer)。

并发集合为标准的集合接口提供了高性能的并发实现,这些实现在内部自己管理同步。因此,并发集合中不可能排除并发活动;将它锁定没有什么作用,只会使程序的速度变慢。

70,线程安全性的文档化

如果你没有在一个类的文档中描述其行为的并发情况,使用这个类的程序员将不得不做出某些假设。如果这些假设是错误的,这样得到的程序就可能缺少足够的同步,或者过度同步。无论属于哪种情况,都可能会发生严重的错误。

一个类为了可被多个线程安全使用,必须在文档中清楚地说明它所支持的线程安全性级别。

  • 不可变的——这个类的实例是不可变的。这样的例子包括String,Long,BigInteger。
  • 无条件的线程安全——这个类的实例是可变的,但是这个类有足够的内部同步。例子包括Random,ConconcurrentHashMap。
  • 有条件的线程安全——除了有些方法为进行安全的并发使用而需要外部同步之外,这种线程安全级别与无条件安全相同。例子包括:Collections.synhronized包装返回的集合,它们的迭代器要求外部同步。
  • 非线程安全——这个类的实例是可变的。为了并发使用它们,客户必须利用自己选择的外部同步包围每个方法调用。例子包括ArrayList
  • 线程对立的——这个类不能安全地被多个线程并发使用,即使所有的方法调用都被外围同步包围。

类的线程安全说明通常放在它的文档中,但带有特殊线程安全属性的方法则应该在它们自己的文档注释中说明它们的属性。

私有锁只能用在无条件的线程安全类上。私有锁对象模式特别适用于那些专门为继承而设计的类。如果这种类适用它的实例作为锁的对象,子类可能很容易在无意中妨碍基类的操作,反之亦然。

61,慎用延迟初始化

延迟初始化是延迟到需要域的值时才将它初始化的这种行为。

对于延迟初始化,最好建议“除非绝对必要,否则就不要那么做”。延迟化降低了初始化类或者创建实例的开销,却增加了访问被延迟初始化的域的开销。

如果域只是在类的实例部分被访问,并且初始化这个域的开销很高,可能就值得进行延迟初始化。

  • 如果出于性能的考虑而需要对静态域使用延迟初始化,就使用lazy initialization holder class 模式。保证在被用时初始化。
  • 如果出于性能的考虑而需要对实例域使用延迟初始化,就使用双重检查模式。这种模式避免了在域被初始化之后访问这个域时的锁定开销。

第一次检查时没有锁定,看看这个域是否被初始化;第二次检查时又锁定。只有当第二次检查时标明这个域没有被初始化,才进行初始化。如果域已经被初始化就不会有锁定,域被声明为volatile很重要。

72,不要依赖于线程调度器

当有多个线程可以运行时,由线程调度器决定哪些线程将会运行,以及运行多长时间。

任何依赖于线程调度器来达到正确性或者性能要求的程序,很有可能都是不可移植的。

要编写健壮,响应良好的,可移植的多线程应用程序,最好的办法是确保可运行线程的平均数量不明显多于处理器的数量。

线程优先级是java平台上最不可移植的特征了。

73,避免使用线程组

线程组并没有提供太多有用的功能,而且他们提供的许多功能还都有缺陷的。

如果你正在设计的一个类需要处理线程的逻辑组,或许就应该使用线程池executor。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容

  • 一.线程安全性 线程安全是建立在对于对象状态访问操作进行管理,特别是对共享的与可变的状态的访问 解释下上面的话: ...
    黄大大吃不胖阅读 836评论 0 3
  • Thread机制允许同时进行的多个活动,并发程序设计比单线程程序设计要困难得多。 第六十六条、同步访问共享的可变数...
    Timorous阅读 232评论 0 0
  • layout: posttitle: 《Java并发编程的艺术》笔记categories: Javaexcerpt...
    xiaogmail阅读 5,805评论 1 19
  • 曾几何时,讨厌大太阳,干燥又毒辣……几乎每天都躲在宿舍不敢出门,一出门就打伞,打伞成了我们当时出门的习惯。 其实,...
    躺着仰望天空阅读 234评论 2 0
  • 今天主要给大家推荐四款用于切图的工具和一个在线移动图标生成工具,主要的应用场景是根据一张1024*1024的大图很...
    KODIE阅读 5,228评论 3 6