Java Concurrency / Multithreading Tutorial
【原文地址】 原文作者: Jakob Jenkov
以前的计算机只有一个CPU,一次只能执行一个程序。 后来发展出多任务处理(任务/进程)模型。 但这个模型还不是真正意义上的同时执行,它仅仅是让单CPU共享给多个程序使用。 操作系统将CPU划分出时间片,供给多个程序使用。操作系统负责调度时间片,并完成不同程序运行状态的切换(保存上一个程序的状态,恢复下一个将要执行的程序状态)。
随着多任务处理技术的发展,给软件开发人员带来了新的挑战。程序将不再认为自己能够独占CPU,内存或者其他计算机资源。一个好的程序应该在完成之后释放所有的资源,让其他的程序可以继续使用这些资源。
再后来就发展出在同一个程序中可以执行多个线程,这就是 多线程 模型。 一个线程的执行可以被视为一个程序在执行中的CPU。(线程是程序所能使用到的CPU最小调度单元) 当在同一个程序中运行多个线程时,对于程序来说,就仿佛是同时拥有了多个CPU。
对于一些程序来说,为了提高性能,多线程 是一个很不错的方法。 然而,多线程 相对多任务 会带来更多的挑战。 线程是运行在同一个程序中的,并且可以同时读/写相同的内存区域。(进程可以内存隔离) 这就会遇到原本在单线程程序中没见过的各种问题。 现代计算机,通常都是有多核的CPU,甚至是多个多核CPU(多路)。 这就意味着不同的线程可能会被调度到不同的CPU,不同的内核上执行。
如果一个线程读取一个内存地址恰巧是另一个线程正要写入的,那第一个线程读取到的数据会是怎样的? 会是旧值?还是第二个线程写入的数据?亦或者两个数据的混合? 而如果有两个线程同时对同一个内存地址写入数据,那到底谁的数据写入,谁的数据被丢弃呢? 是写入第一个线程的数据?还是写入第二个线程的数据?亦或是写入一个混合数据?
如果没有一个恰当的预防措施,那上面的问题都可能遇到。 甚至会是一个不可预见的结果。 而且每一次的结果都可能不一样。 因此,对开发人员来说,重要的是去正确的处理。Java并发技术中的一个主题,就是去学习如何控制线程访问内存/文件/数据库等共享资源。
Java中的多线程并发
开发人员最常见的,用于开发多线程的语言就是java了。 java从一开始就拥有多线程的能力。 因此,java开发人员经常遇到上面描述的问题。 这也是作者编写这个文章的主要原因。 同时也作为一份笔记(我也是当作学习笔记的),用于记录一些对其他java开发人员有益的内容。
主要是关于java的多线程并发的内容。但是,考虑到多任务以及分布式系统也会遇到类似多线程的问题,所以,也会包含一部分多任务,分布式系统的内容。 因此,并发将不仅仅指代多线程。
2015年以后的java并发
自从第一本java并发编程书籍出版,以及java5中的并发工具包(JUC)发布以来,并发世界中产生了很多新的架构与设计。
如今,异步“shared-nothing”平台(典型就是:Hadoop)以及诸如:Vert.x,Play / Akka,Qbit这类的API的出现。 这些平台架构使用了一个与标准Java/JEE并发模型截然不同的并发模型,包括线程,共享内存以及锁。 如今,已经有了非阻塞并发算法,而且,新的非阻塞工具也已经加入我们的工具集了,如:LMax的Disrupter。 Java7也引入了函数式编程的并行处理模型:Fork/Join;以及Java8的集合流式API。
随着与时俱进的技术发展,作者也更新了Java Concurrency tutorial。 因此,这份指南再一次开工,只要作者有时间,就会编写点新的内容。