码迷,mamicode.com
首页 > 编程语言 > 详细

java并发编程实战--读后总结

时间:2018-08-29 21:22:26      阅读:244      评论:0      收藏:0      [点我收藏+]

标签:throw   解决方案   可见性   ado   实战   tom   而且   清除   单线程   

1.简介

  1.1并发简史

    产生原因:资源利用率、公平性、便利性

  1.2线程的优势

    1.2.1发挥多处理器的强大功能

    1.2.2建模的简单性

    1.2.3异步事件的简化处理

    1.2.4响应更灵敏的用户界面

  1.3线程带来的风行

    1.3.1安全性问题

      永远不发生糟糕的事情

    1.3.2活跃性问题

      某件正确的事情最终会发生

    1.3.3性能问题

      希望正确的事情尽快发生

  1.4线程无处不在

    所使用的框架可能会创建线程

    Timer类,用于延迟运行任务一次或多次,确保访问对象是线程安全的

    Servlet和JSP,需要保证多个Servlet共享对象的线程安全

    远程方法调用

    Swing和AWT

2.线程安全性

  2.1什么是线程安全性

    多个线程访问某个类时,这个类始终都能表现出正确的行为,这个类是线程安全的

    无状态对象一定是线程安全的

  2.2原子性

    2.2.1竞态条件

      当计算的正确性取决于线程的执行时序时,就会发生竟态条件

      基于失效的观察结果做出判断的竟态条件称为“先检查后执行”

    2.2.2示例:延迟初始化中的竞态条件

    2.2.3复合操作

      将“先检查后执行”和“读取-修改-写入”等操作统称为复合操作

  2.3加锁机制

    2.3.1内置锁

    2.3.2重入

  2.4用锁来保护状态

  2.5活跃性与性能

    当执行时间较长的计算或者可能无法快速完成的操作时,一定不要持有锁

3.对象的共享

  3.1可见性

    3.1.1失效数据

      获取到一个未更新的值

    3.1.2非原子的64位操作

      获取的失效数据是之前的值而非随机值是最低安全性,但不适用于非volaties类型的64位数值变量,因为JVM会把64位的读操作和写操作分解为2个32位的操作

      在多线程中使用共享且可变的long和double等类型的变量是不安全的,除非用volatie声明或锁保护

    3.1.3加锁与可见性

    3.1.4Volatile变量

      Volatile声明的变Volatile变量量是共享的,读取Volatile变量总能获取最新的值

      但是Volatile变量比较脆弱,仅当Volatile变量能简化代码的失效以及对同步策略的验证时才应该使用它们

      加锁机制可以确保可见性和原子性,Volatile变量只能确保可见性

  3.2发布与逸出

    发布:使对象能够在当前作用域之外的代码中使用

    逸出:不应该被发布的对象被发布时称为逸出

    安全的对象构造过程

      不要在构造过程使用this逸出

      使用工厂来放在this引用在构造过程中逸出

  3.3线程封闭

    3.3.1Ad-hoc线程封闭

      维护线程封闭性的职责完全由程序实现,但是具有脆弱性,尽量少用

    3.3.2栈封闭

    3.3.3ThreadLocal类

      使线程中的值与保存值的对象关联起来,总能获取到最新值

      ThreadLocal变量类似于全局变量,降低了代码的可重用性,在类之间引入了隐含的耦合性

  3.4不变性

    3.4.1Final域

    3.4.2示例:使用Volatile类型来发布不可变对象

  3.5安全发布

    3.5.1不正确的发布:正确的对象被破坏

    3.5.2不口不对象与初始化安全性

    3.5.3安全发布的常用模式

      在静态初始化函数中初始化一个对象引用

      将对象的引用保存到volatile类型的域或者AtomicReferance对象中

      将对象的引用保存到某个正确构造对象的final类型域中

      将对象的引用保存到一个由锁保护的域中

    3.5.4事实不可变对象

      对象在技术上看是可变,但是其状态在发布后不会再改变的对象称为“事实不可变对象”

    3.5.5可变对象

      不可变对象可以任意发布

      事实不可变对象通过安全方式发布

      可变对象通过安全方式发布,并且必须是线程安全的或者锁保护起来

    3.5.6安全的共享对象

      策略:线程封闭、只读共享、线程安全共享、保护对象 

4.对象的组合

  4.1设计线程安全的类

    4.1.1收集同步需求

    4.1.2依赖状态的操作

    4.1.3状态的所有权

  4.2实例封闭

    4.2.1Java监视器模式

    4.2.2实例:车辆追踪

  4.3线程安全性的委托

    4.3.1示例:基于委托的车辆追踪器

    4.3.2独立的状态变量

    4.3.3当委托失效时

    4.3.4发布底层的状态变量

    4.3.5示例:发布状态的车辆追踪器

  4.4在现有的线程安全类中添加功能

    4.4.1客户端加锁机制

      对使用某个对象X的客户端代码,使用X本身用于保护其状态的锁来保护这段客户端代码

      若没有则添加的正确的辅助类

public class ListHelper<E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<>());

    public boolean putIfAbsent(E x) {
        synchronized (list) {//注意加锁的对象
            boolean absent = !list.contains(x);
            if (absent) {
                list.add(x);
            }
            return absent;
        }
    }
}

      会破坏同步策略的封装性

    4.4.2组合

public class ImprovedList<T> implements List<T> {
    private final List<T> list;

    public ImprovedList(List<T> list) {
        this.list = list;
    }

    public synchronized boolean putIfAbsent(T x) {
        boolean contains = list.contains(x);
        if (contains) {
            list.add(x);
        }
        return !contains;
    }
}

  4.5将同步策略文档化

    解释含糊的文档

5.基础构建模块

  5.1同步容器类

    5.1.1同步容器类的问题

    5.1.2迭代器与Concurrent-ModificationException

    5.1.3隐藏迭代器

  5.2并发容器

    5.2.1ConcurrentHashMap

    5.2.2额外的原子Map操作

    5.2.3CopyOnWriteArrayList

      当迭代操作远远多于修改操作时才应该使用“写入时复制”容器

  5.3阻塞队列和生产者-消费模式

    5.3.1示例:桌面搜索

    5.3.2串行线程封闭

    5.3.3双端队列与工作密取

  5.4阻塞方法与中断方法

    恢复中断

  5.5同步工具类

    5.5.1闭锁

      延迟线程的进度直到其到达终止状态,用于确保某些活动直到其他活动都完成才执行

public class TestHarness {
    public long timeTasks(int nThrads, final Runnable task) throws InterruptedException {
        final CountDownLatch startGate = new CountDownLatch(1);//起始门
        final CountDownLatch endGate = new CountDownLatch(nThrads);//结束门
        for (int i = 0; i < nThrads; i++) {
            Thread t = new Thread() {
                public void run() {
                    try {
                        startGate.await();//等待所有线程准备就绪
                        try {
                            task.run();
                        } finally {
                            endGate.countDown();//完成一件事情就减1
                        }
                    } catch (InterruptedException ignored) {

                    }
                }
            };
            t.start();
        }
        long start = System.nanoTime();
        startGate.countDown();
        endGate.await();
        long end = System.nanoTime();
        return end - start;
    }
}

    5.5.2FutureTask

      用于闭锁

    5.5.3信号量

      计数信号量用于控制同时访问特定资源的操作数量

    5.5.4栅栏

      所有线程都必须同时到达栅栏才能继续执行,栅栏可以重置复用

  5.6构建高效且可伸缩的结果缓存

6.任务执行

  6.1在线程中执行任务

    6.1.1串行地执行任务

    6.1.2显示地为任务创建线程

    6.1.3无限制创建线程的不足

      线程生命周期的开销非常高

      资源消耗大

      稳定性差

  6.2Executor框架

    6.2.1示例:基于Executor的Web服务器

public class Test {
    private static final int NTHREADS = 100;
    private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);

    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(8080);
        while (true) {
            final Socket connection = socket.accept();
            Runnable task = new Runnable() {
                public void run() {
                    handleRequest(connection);
                }
            };
            exec.execute(task);
        }
    }
}

    6.2.2执行策略

    6.2.3线程池

      newFixedThreadPool()创建固定长度的线程池

      newCachedThreadPool()创建可缓存的线程池,当前规模超过处理需求时,回收空闲线程,需求增加时,添加新线程,规模大小无限制

      newScheduledThreadPool()以延迟或定时方式执行任务的固定线程池

    6.2.4Executor的生命周期

    6.2.5延迟任务与周期任务

      Timer类精准性有问题,而且抛出异常就会被取消

      newScheduledThreadPool是基于相对时间的,用于间隔时间的执行的任务,例如1分钟检查一次活动

  6.3找出可利用的并行性

    6.3.1示例:串行的页面渲染器

    6.3.2携带结果的任务Callable与Future

    6.3.3示例使用Future实现页面渲染器

    6.3.4在异构任务并行化中存在的局限

    6.3.5CompletionService:Executor与BlockingQueue

    6.3.6示例:使用CompletionService实现页面渲染器

    6.3.7为任务设置时限

    6.3.8示例:旅行预定门户网站

      invokeAll()可以将按照任务集合中迭代器的顺序将所有Future添加到返回的集合中,当所有任务执行完毕或中断或超时,invokeAll将返回

7.取消与关闭

  7.1任务取消

    7.1.1中断

      用户请求取消

      有时间限制的操作,搜索任务超时,取消正在搜索的任务

      应用程序事件,解决方法找到时,取消其他搜索解决方案的任务

      错误

      关闭,程序或服务器关闭

      发出中断请求,线程在合适的时刻中断自己

    7.1.2中断策略  

    7.1.3响应中断

    7.1.4示例:计时运行

    7.1.5通过Future来实现取消

    7.1.6处理不可中断的阻塞

      Java.io包中的同步Socket I/O

      Java.io包中的同步I/O

      Selector的异步I/O

      获取某个锁

    7.1.7才有newTaskFor来封装非标准的取消

  7.2停止基于线程的服务

    7.2.1示例:日志服务

    7.2.2关闭ExecutorService

    7.2.3“毒丸”对象

      当队列得到这个“毒丸”对象,立即停止

    7.2.4示例:只执行一次的服务

    boolean checkMail(Set<String> hosts, long timeout, TimeUnit unit) throws InterruptedException {
        ExecutorService exec = Executors.newCachedThreadPool();
        final AtomicBoolean hasNewMail = new AtomicBoolean((false));
        try {
            for (final String host : hosts) {
                exec.execute(new Runnable() {
                    public void run() {
                        if (checkMail(host)) {
                            hasNewMail.set(true);
                        }
                    }
                });
            }
        } finally {
            exec.shutdown();//安全的关闭
            exec.awaitTermination(timeout, unit);
        }
        return hasNewMail.get();
    }

    7.2.5shuadownNow的局限性

  7.3处理非正常的线程终止

    未捕获一次的处理

  7.4JVM关闭

    7.4.1关闭钩子

      通过Runtime.addShutdownHook注册的但尚未开始的线程

      用于实现服务或应用程序的清理工作

    7.4.2守护线程

      主线程创建的所有线程都是普通线程

      主线程创建之外的所有线程都是守护线程

    7.4.3终结器

8.线程池的使用

  8.1在任务与执行处理之间的耦合

    8.1.1线程饥饿死锁

      一个线程无期限的等待其他线程的资源或条件会发送线程饥饿死锁

    8.1.2运行时间较长的任务

  8.2设置线程池的大小

  8.3配置ThreadPoolExecutor

    8.3.1线程的创建与销毁

    8.3.2管理队列任务

    8.3.3饱和策略

      “中止”策略,抛弃下一个将要被执行的任务

      “调用者运行”策略,将任务回退给调用者

    8.3.4线程工厂

    8.3.5在调用构造函数后再定制ThreadPoolExecutor

  8.4扩展ThreadPoolExecutor

    示例:给线程池添加统计信息

  8.5递归算法的并行化

    示例:谜题框架

9.图形用户界面应用程序

  9.1为什么GUI是多线程的

    9.1.1串行事件处理

    9.1.2Swing中的线程封闭机制

  9.2短时间的GUI任务

  9.3长时间的GUI任务

    9.3.1取消

    9.3.2进度标识和完成标识

    9.3.3SwingWorker

  9.4共享数据模型

    9.4.1线程安全的数据模型

    9.4.2分解数据模型

  9.5其他形式的单线程子系统

10.避免活跃性危险

  10.1死锁

    10.1.1锁顺序死锁

    10.1.2动态的锁顺序死锁

    10.1.3在协作对象之间发送的死锁

    10.1.4开放调用

      调用方法时不持有锁,这种调用称为开放调用

    10.1.5资源死锁

  10.2死锁的避免与诊断

    10.2.1支持定时的锁

    10.2.2通过线程转储信息来分析死锁

  10.3其他活跃性危险

    10.3.1饥饿

      要避免使用线程优先级,会增加平台依赖性

    10.3.2糟糕的响应性

    10.3.3活锁

      多个相互协作的线程对彼此进行响应从而修改各自的状态,使得任何一个线程都无法继续执行时,就发生了活锁

      通过等待随机长度的时间和回退可以有效的避免活锁的发生

11.性能与可伸缩性

  11.1对性能的思考

    11.1.1性能与可伸缩性

      运行速度指标:服务器时间、等待时间

      处理能力指标:生产量、吞吐量

      可伸缩性:增加计算资源时,程序的吞吐量或者处理能力相应地增加

    11.1.2评估各种性能权衡因素

  11.2Amdahl定律

    11.2.1示例:在各种框架中隐藏的串行部分

    11.2.2Amdahl定律的应用

      准确估计出执行过程中串行部分所占的比例

  11.3线程引入的开销

    11.3.1上下文切换

      可运行的线程大于CPU数量时,保存当前运行线程的执行上下文,新调度进来的线程执行上下文设置为当前上下文

    11.3.2内存同步

    11.3.3阻塞

  11.4减少锁的竞争

    11.4.1缩小锁的范围("快进快出")

    11.4.2减小锁的粒度

    11.4.3锁分段

    11.4.4避免热点域

    11.4.5一些替代独占锁的方法

    11.4.6监测CPU的利用率

      负载不充足

      I/O密集

      外部限制

      锁竞争

    11.4.7向对象池说“不”

  11.5示例:比较Map的性能

  11.6减少上下文切换的开销

12.并发程序的测试

  12.1正确性测试

    12.1.1基本的单元测试

    12.1.2对阻塞操作的测试

    12.1.3安全性测试

    12.1.4资源管理的测试

    12.1.5使用回调

    12.1.6产生更多的交替操作

  12.2性能测试

    12.2.1在PutTakeTest增加计时功能

    12.2.2多种算法的比较

    12.2.3响应性衡量

  12.3避免性能测试的陷阱

    12.3.1垃圾回收

    12.3.2动态编译

    12.3.3对代码路径的不真实采样

    12.3.4不真实的竞争程度

    12.3.5无用代码的清除

  12.4其他的测试方法

    12.4.1代码审查

    12.4.2静态分析工具

    12.4.3面向方面的测试技术

    12.4.4分析与监测工具

13.显示锁

  13.1Lock与ReentrantLock

    13.1.1轮询锁与定时锁

    13.1.2可中断的锁获取操作

    13.1.3非块结构的加锁

  13.2性能考虑因素

  13.3公平性

  13.4在synchronized和ReentrantLock之间进行宣传

  13.5读-写锁

14.构建自定义的同步工具

  14.1状态依赖性的管理

    14.1.1示例:将前提条件的失败传递给调用者

    14.1.2示例:通过轮询与休眠来实现简单的阻塞

    14.1.3条件队列

  14.2使用条件队列

    14.2.1条件谓词

    14.2.2过早唤醒

    14.2.3丢失的信号

    14.2.4通知

    14.2.5示例:阀门类

    14.2.6子类的安全问题

    14.2.7封装条件队列

    14.2.8入口协议与出口协议

  14.3显示的Condition对象

  14.4Synchronized剖析

  14.5AbstractQueuedSynchronizer

  14.6java.util.concurrent同步器类的AQS

    14.6.1ReentrantLock

    14.6.2Semaphore与CountDownLatch

    14.6.3FutureTask

    14.6.4ReentrantReadWriteLock

15.原子变量与非阻塞同步机制

  15.1锁的劣势

  15.2硬件对并发的支持

    15.2.1比较并交换

    15.2.2非阻塞的计数器

    15.2.3JVM对CAS的支持

  15.3原子变量类

    15.3.1原子变量是一种更好的volatile

    15.3.2性能比较:锁与原子变量

  15.4非阻塞算法

    15.4.1非阻塞的栈

    15.4.2非阻塞的链表

    15.4.3原子的域更新器

    15.4.4ABA问题

16.Java内存模型

  16.1什么是内存模型,为什么需要它

    16.1.1平台的内存模型

    16.1.2重排序

    16.1.3Java内存模型简介

    16.1.4借助同步

  16.2发布

    16.2.1不安全的发布

    16.2.2安全的发布

    16.2.3安全初始化模式

    16.2.4双重检查加锁

  16.3初始化过程中的安全性

java并发编程实战--读后总结

标签:throw   解决方案   可见性   ado   实战   tom   而且   清除   单线程   

原文地址:https://www.cnblogs.com/plxz/p/9530500.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!