【笔记】java并发编程实战 (2)

在设计线程安全类的过程中,需要包含一下三个基本要素:a)找出构成对象状态的所有变量b)找出约束状态变量的不变性条件c)建立对象状态的并发访问管理策略

等待某个条件为真的各种内置机制(包括等待和通知等机制)都与内置加锁机制紧密关联

将数据封装在对象内部,可以将数据的访问限制在对象的方法上,从而更容易确保线程在访问数据时总能持有正确的锁

使用私有的锁对象而不是对象的内置锁(或任何其他可通过公有方式访问的锁),可以将锁封装起来,使客户代码无法得到锁,但客户代码可以通过共有方法来访问锁

如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,那么就可以安全的发布这个变量

Synchronized、volatile或者任何一个线程安全类都对应于某种同步策略,用于在并发访问时确保数据的完整性

在设计同步策略时需要考虑多个方面,例如,将哪些变量声明为volatile类型,哪些变量用锁来保护,哪些锁保护那些变量,哪些变量必须是不可变的或者被封闭在线程中的,哪些操作必须是原子操作等。

servletContext、Httpsession或dataSource等的线程安全性

同步容器将所有对容器状态的访问都穿行化,以实现它们的线程安全性。这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重减低

通过并发容器来代替同步容器,可以极大的提高伸缩性并降低风险;ConcurrentHashMap、CopyOnWriteArrayList、CopyonWriteArraySet、BlockingQueue

阻塞队列可以作为同步工具类,其他类型的同步工具类还包括信号量(Semaphore)、栅栏(Barrier)以及闭锁(Latch)

闭锁可以延迟线程的进度直到其到达终止状态,可以用来确保某些活动直到其他活动都完成偶才继续执行

FutureTask表示的计算是通过Callable来实现的,相当于一种可生成结果的Runnable,FutureTask在Executor框架中表示异步任务,此外还可以用来表示一些时间较长的计算

计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量,可以用于实现资源池,例如数据库连接池

栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生。栅栏与闭锁的关键区别在于,所有线程必须同时到达栅栏位置,才能继续执行,闭锁用于等待事件,而栅栏用于等待其他线程

并发技巧:a)可变状态是至关重要的b)尽量将域声明为final类型,除非需要它们是可变的c)不可变对象一定是线程安全的d)封装有助于管理复杂性e)用锁来保护每个可变变量f)当保护同一个不变性条件中的所有变量时,要使用同一个锁g)在执行复合操作期间,要持有锁h)如果从多个线程中访问同一个可变变量时没有同步机制,那么程序会出现问题i)不要故作聪明的推断出不需要使用同步j)在设计过程中考虑线程安全,或者在文档中明确地指出它不是线程安全的k)将同步策略文档化

在线程池中执行任务比为每个任务分配一个线程优势更多:a)重用线程,分摊在线程创建和销毁过程中产生的巨大开销b)请求到达时,工作线程已存在,不会由于等待创建线程而延迟任务的执行,提高了响应性c)通过调整线程池大小,可以创建足够多的线程以便使处理器保持忙碌状态,同时还可以防止过多线程相互竞争资源而使应用程序耗尽内存或失败

通过使用Executor,可以实现各种调优、管理、监视、记录日志、错误报告和其他功能,如果不使用任务执行框架,那么要增加这些功能是很困难的

Executor框架将任务提交与执行策略解耦开来,同时还支持多种不同类型的执行策略。当需要创建线程来执行任务时,可以考虑使用Executor

在java中没有一种安全的抢占式方法来停止线程,因此也就没有安全的抢占式方法来停止任务。只有一些协作式的机制,使请求取消的任务和代码都遵循一种协商好的协议:a)”已请求取消”标志

对中断操作的正确理解是:它并不会真正地中断一个正在运行的线程,而只是发出中断请求,然后由线程在下一个合适的时刻中断自己(这些时刻也被称为取消点),通常,中断是实现取消的最合理方式

最合理的中断策略是某种形式的线程级(Thread-Level)取消操作或服务级(Service-Level)取消操作:尽快退出,在必要时进行清理,通知某个所有者线程已经退出

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjgps.html