Java并发的四句箴言

Wesley13
• 阅读 469

关注本号,教你更多Java技能

1.不要这样做

2.没有什么是真的,一切可能都有问题

3.它起作用,并不意味着它没有问题

4.你仍然必须理解它

1.1 不要使用它

避免并发副作用的最简单方法就是不用。虽然它似乎足够安全,但它存在无数微妙的陷阱。

证明并发性的唯一因素是速度。只是希望运行更快是不合理的 - 首先应用一个分析器来发现你是否可以执行其他一些优化。

如果被迫并发,请采取最简单安全的实现。使用已有库并尽可能少写自己的代码。有了并发,就没有什么“简单事”。

1.2 一切都可能有问题

没有并发性的世界有一定的顺序和一致性。通过简单地将变量赋值给某个值,很明显它应该始终正常工作。

而在并发领域,必须质疑一切。即使将变量设置为某个值也可能不会按预期的方式工作。

在非并发程序中你可以忽略的各种事情突然变得非常重要。
例如,你必须知道处理器缓存以及保持本地缓存与主内存一致的问题。
必须了解对象构造的深度复杂性,以便你的构造对象不会意外地将数据暴露给其他线程更改。

1.3 起作用不意味着没问题

很容易编写出一个看似完美,实则有问题的并发程序,往往问题在极端情况下才暴露 - 在部署后不可避免地出现用户问题。

  • 你无法证明并发程序是正确的,只能(有时)证明它是不正确的

  • 大多数情况下即使它有问题,你可能也无法检测到

  • 你通常无法编写有用的测试,因此必须依靠代码检查结合深入的并发知识来发现错误

  • 即使是有效的程序也只能在其设计参数下工作。当超出这些设计参数时,大多数并发程序会以某种方式失败。

在其他Java主题中,我们培养了一种感觉-决定论。一切都按照语言的承诺(或隐含)进行,这是令人欣慰和期待的 - 毕竟,编程语言的目的是让机器做我们想要的。
从确定性编程的世界进入并发编程领域,有种称为Dunning-Kruger效应的认知偏差
概括为“你知道得越少,你以为你知道得越多。”这意味着,相对不熟练的人拥有着虚幻的自我优越感,错误地评估他们的能力远高于实际。

无论你多么确定代码是线程安全的,它可能已经无效了。你可以很容易地了解所有的问题,然后几个月或几年后你会发现一些概念让你意识到你编写的大多数内容实际上都容易受到并发错误的影响。当某些内容不正确时,编译器不会告诉你。为了使它正确,你必须在研究代码时在脑里模拟所有并发问题。

在Java的所有非并发领域,“没有明显的错误和没有明显的编译错误”似乎意味着一切都好。而对于并发,没有任何意义。

1.4 必须理解

在格言1-3之后,你可能会对并发性感到害怕,并且认为,“到目前为止,我已经避免了它,也许我可以继续避免。

你可能知道其他编程语言更好地设计用于构建并发程序 - 甚至是在JVM上运行的程序,例如Clojure或Scala。为什么不用这些语言编写并发部分并将Java用于其他所有部分呢?

唉,你不能轻易逃脱,即使你从未明确地创建一个线程,但可能你使用的框架创建了 - 例如,Swing或者像Timer 定时器。
最糟糕的事情:当你创建组件,你必须假设这些组件可能在多线程环境中重用。即使你的解决方案是放弃并声明你的组件“非线程安全”,你仍然必须知道这样的声明是重要的,它是什么意思

人们有时会认为并发性太难,不能包含在介绍语言的书中。认为并发是一个独立主题,在日常编程中出现的少数情况(例如图形用户界面)可以用特殊的习语来处理。如果你可以避免它,为什么要介绍这样的复杂的主题。
不幸的是,你无法选择何时在Java程序中出现线程。你从未写过自己的线程,并不意味可以避免编写线程代码。例如Web系统本质上是多线程的Web服务器通常包含多个处理器,而并行性是利用这些处理器的理想方式。这样的系统看起来简单,必须理解并发才能正确地编写它。

Java是一种多线程语言,肯定存在并发问题。因此,有许多Java程序正在使用中,或者只是偶然工作,或者大部分时间工作并且不时地发生问题。有时这种问题是相对良性的,但有时它意味着丢失有价值的数据,如果你没有意识到并发问题,你最终可能会把问题放在其他地方而不是你的代码。如果将程序移动到多处理器系统,则可以暴露或放大这类问题。基本上,了解并发性使你意识到正确的程序可能会表现出错误的行为。

2 残酷的真相

Java是在充满自信,热情和睿智的氛围中创建的。在发明一种编程语言时,很容易就像语言的初始可塑性会持续存在一样,你可以把某些东西拿出来,如果不能解决问题,那么就修复它。一旦人们开始使用你的语言,变化就会变得更加严重。
语言设计的过程本身就是一门艺术。
通过匆忙设计语言而产生的认知负荷和技术债务最终会赶上我们。

Turing completeness是不足够的;语言需要更多的东西:它们必须能够创造性地表达,而不是用不必要的东西来衡量我们。解放我们的心理能力只是为了扭转并再次陷入困境,这是毫无意义的。我承认,尽管存在这些问题,我们已经完成了令人惊奇的事情,但我也知道如果没有这些问题我们能做得更多。

热情使原始Java设计师因为看起来有必要而投入功能。信心(以及原始语言)让他们认为任何问题都可以解决。
有人认为任何加入Java的东西是固定的和永久性的 - 这是非常有信心,相信第一个决定永远是正确的,因此我们看到Java的体系中充斥着糟糕的决策。其中一些决定最终没有什么后果,例如你可以告诉人们不要使用Vector,但保留了对之前版本的支持。

线程包含在Java 1.0中。当然,并发性是影响语言的基本语言设计决策,很难想象以后才添加它,客观的说,当时并不清楚基本的并发性。像C能够将线程视为一个附加功能,因此Java设计师也纷纷效仿,包括一个Thread类和必要的JVM支持。
C语言是原始的,这限制了它的野心。这些限制使附加线程库合理。当采用原始模型并将其粘贴到复杂语言中时,Java的大规模扩展迅速暴露了基本问题。在Thread类中的许多方法的弃用以及后续的高级库浪潮中,这种情况变得明显,这些库试图提供更好的并发抽象。

为了在高级语言中获得并发性,所有语言功能都会受到影响,例如标识符为可变值。在函数和方法中,所有不变和防止副作用的方法都会导致简化并发编程(纯函数式编程语言基础)的变化,但当时对于主流语言的创建者来说似乎是奇怪的想法。最初的Java设计师要么对这些选择有所了解,要么认为它们太不同了,并且会抛弃许多潜在的语言采用者。语言设计社区当时根本没有足够的经验来理解调整在线程库中的影响。

Java经历告诉我们,结果是相当灾难性的。程序员很容易陷入认为Java 线程并不那么困难的陷阱。工作的程序充满了微妙的并发bug。
为了获得正确的并发性,语言功能必须从头开始设计并考虑并发性。Java将不再是为并发而设计的语言,而只是一种允许它的语言。
尽管有这些基本的不可修复的缺陷,Java的后续版本添加了库,以便在使用并发时提升抽象级别。事实上,我根本不会想到有可能在Java 8中进行改进:并行流和CompletableFutures 史诗般的变化。

Java并发的四句箴言

逢考必过

Java并发的四句箴言

高考加油

本文分享自微信公众号 - JavaEdge(Java-Edge)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
2星期前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
2年前
Java日期时间API系列31
  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
6个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这