读书笔记 JAVA 并发编程实战 第一章 简介
简介
早期的计算机不包含操作系统,从头到尾只能执行一种程序。
操作系统的出现 使得计算机能够同时运行多个程序:
- 资源利用率:等待的同时运行另一个程序
- 公平性:多个用户,时间片轮转
- 便利性:每个程序执行一个任务,并且在必要的时候互相通信,这样更加高效。
线程成为轻量级进程,大多数操作系统,以线程为基本的调度单位。如果没有明确的协同机制,多个线程彼此独立。
线程可以有效的减低开发和维护成本,提高复杂引用的性能,特别会GUI反应的灵敏度。并且简化JVM实现。垃圾收集器通常在一个或多个专门的线程中运行。
建模的简单性
只包含一种工作更加的容易编写,测试。 如果每一中任务都分配一个专门的线程,那么可以形成一种串行执行的假象。通过线程,可以将复杂并且异步的工作流分解为一组简单并且同步的工作流(工作调度)
我们可以通过现有的框架实现上述目标,框架负责帮助我们解决细节问题,例如请求管理,线程创建,负载平衡。并且在正确的时刻将请求分发给正确的应用程序组件。
异步时间的简化处理
服务器应用程序在接收到多个远程连接,会分配各自线程并且同步I/O。如果某个应用程序对套接字执行操作而此时数据没到来,那么就会一直阻塞,直到数据到达。
单线程时,意味着所有的处理都会停顿。所以单线程必须使用非阻塞I/O。
Eg:非阻塞I/O:用户进程其实是需要不断的主动询问kernel数据准备好了没有。
响应灵敏的用户界面
现代GUI框架 大多使用一个事件分发线程。单线程子系统会一直处于主事件循环中(长时间任务)。造成反应不灵敏。
解决方法:长时间动作使用单独线程
安全性问题
线程安全性很复杂,一般很难预测。 安全性的意思是,永远不会出问题
标注也很重要:
@NotThreadSafe
@ThreadSafe 说明了可以在多线程环境下使用这个类。
活跃性问题: 无限循环使得之后的代码无法得到执行。后面的代码永远无法的到资源,并且活跃性问题依赖于不同线程的事件发生时序,因此在开发或测试中并总能重现
性能问题
活跃性意味着正确的事情总会发生,但是不够好。
性能问题包含:服务时间过长,响应不灵敏,吞吐率过高,资源消耗过高,可伸缩性太低。
线程无处不在
即使程序中没有显示的创建线程。框架中可能会创建线程。这些线程调用的代码必须是线程安全的。不然会带来沉重的负担。
当JVM启动时,会为JVM内部任务创建后台线程,并传建一个主线程来运行main方法。几乎所有的java程序都是多线程的。
当某个框架引入并发性,不能将并发仅仅局限在框架代码。因为框架本身要回到应用程序的代码。而这些代码将访问应用程序的状态。所以考虑时,需要延伸到需要访问这些代码所访问的应用程序状态的所有代码路径。
这些是应用程序之外的线程中调用应用程序的代码,线程安全性需求会延伸到整个应用程序:
- Timer:任务运行一次,或周期性运行。
- Servlet 和JSP: Servlet 用来分发来自HTTP的请求。每个Servlet都是一个程序逻辑组件。
- Servlet同样要满足被多个线程调用。Servlet通常会访问其他Servlet共享的信息。多个Servlet请求共享对象时,必须正确的协同。
- 远程方法调用 RMI:当RMI 调用某个远程方法时,必须对JVM打包到一个字节流。并且通过网络传输给远程的JVM。
远程对象必须注意两个问题:1 正确的协同多个对象中共享的状态。2 对远程对象本身状态的访问。