笔记《Java并发编程实战》[2]

结构化并发应用程序

1任务执行:当围绕“任务执行”来设计应用程序结构时,第一步就是要找出清晰的任务边界。在理想情况下,各个任务之间是相互独立的:任务并不依赖于其他任务的状态、结果或边界效应。【如:大多数服务器应用程序:以独立的客户请求为边界。Web服务器、邮件服务器、文件服务器、EJB服务器以及数据库服务器等,均通过网络接受远程客户的连接请求】

2)任务取消原因:用户请求取消、有时间现在的操作、应用程序事件、错误、关闭。

3取消方式——协作式机制:①设置某个“已请求取消”表中,任务定期查看该标志;②中断,每个线程都有一个boolean类型的中断状态:public void interrupt(){…};public Boolean  isInterrupted(){…}; public static Boolean intertupted(){….};线程中断VS任务中断③Future.cancel()。中断只是个标记,线程不会主动去中断自己,需要自己处理中断。调用阻塞函数期间会抛出InterruptedException。处理InterruptedExceptiona)传递异常,使得你的方法也成为可中断的阻塞方法。b)恢复中断状态,从而是调用栈中的上层代码能够对其进行处理。

“一段来自http://book.51cto.com/art/200704/44732.htm”代码修改:

class MyThread extends Thread
{
	public void run()
	{
		while(!isInterrupted())   // 无限循环,并使线程每隔1秒输出一次字符串
		{ 
			System.out.println(Thread.currentThread().getName()); 
			System.out.println(getName()+" is running"); 
			try{     sleep(1000);}
			catch(InterruptedException e)
			{
				System.out.println(isInterrupted());
				System.out.println(e.getMessage());
				interrupt();			
				System.out.println(isInterrupted());
//break;
			}
		}
	}
}

public class Cs
{
	public static void main(String[] args) throws InterruptedException
	{
		MyThread m=new MyThread();  
		// 创建线程对象m
		System.out.println(Thread.currentThread().getName()); 
		System.out.println("Starting thread...");
		m.start();    
		// 启动线程m
		Thread.sleep(2000);  
		//主线程休眠2秒,使线程m一直得到执行
		System.out.println("Interrupt thread...");
		System.out.println(m.isInterrupted());
		m.interrupt();  
		System.out.println(m.isInterrupted());
		// 调用interrupt()方法中断线程m
		Thread.sleep(2000);   
		// 主线程休眠2秒,观察中断后的结果
		System.out.println("Stopping application..."); // 主线程结束
	}
}

 输出:

 

main
Starting thread...
Thread-0
Thread-0 is running
Thread-0
Thread-0 is running
Interrupt thread...
false
true
false
sleep interrupted
true
Stopping application...

 

④处理不可中断的阻塞:不是所有课阻塞方法或阻塞机制都能响应中断。对于不可中断操作而被阻塞的线程,可以使用类似于中断的手段来停止这些线程,但要求知道线程阻塞的原因,知道抛出那些异常,处理异常处理中断。【如:socket I/O,同步I/OSelector异步I/O,获取某个锁】⑤使用newTaskFor封装非标准的取消:newTaskForThreadPoolExecutor的一个用来生成RunnableFuture的工厂方法,RunnableFuture扩展FutureRunnable接口。可以由它改变Future.cancel方法行为。

4停止基于线程的服务:对于持有线程的服务,只要服务的存在时间大于创建线程的方法的存在时间,那就应该提供生命周期方法关闭自己。

5)处理非正常的线程终止:导致线程提前死亡的最主要原因:RuntimeException。未捕获异常的处理:当一个线程由于未捕获异常而退出是,JVM会把这个事件报告给引用程序提供的UncaughtExceptionHandler异常处理器。

6递归算法的并行化

7GUI是单线程的:通过封闭机制来实现线程安全性。所有GUI对象,包括可视化组件和数据模型等,都只能在事件线程中访问。【单线程的GUI框架并不仅限于Java中,在Qt, NexiStep, MaxOS Cocoa, X Windows以及其他环境中的GUI框架都是单线程的。都是因为会发生竞态条件和死锁:表现在事件处理和MVC

8)串行事件处理:事件是另一种类型的任务。AWTSwing提供的事件处理机制在结构上也类似于ExecutorSwingUtilities.invokeLaterSwingUtilities.invokeAndWait这两个方法的作用酷似Executor。可以将Swing的事件线程视为一个单线程的Executor

9)短时间GUI任务和长时间GUI任务【abstract class SwingWorker<T,V>execute(), doInBackground, process(), done()

======================================================================

活跃性、性能与测试:

1)死锁:锁顺序死锁、动态锁顺序死锁、协作对象之间发生的死锁、资源死锁

2)死锁的避免与诊断:①尽量减少潜在的加锁交互数量,将获取锁时需要遵循的协议写入正式文档并始终遵循这些协议;②通过两阶段策略:先找出在什么地方将获取多个锁(使这个集合尽量小),然后对所有这些实例进行全局分析,从而确保它们在整个程序中获取顺序都保持一致(数据库);③支持定时的锁;④通过线程转储信息来分析死锁;JVM通过线程转储的信息在锁的等待关系图中通过搜索循环来找出死锁。

3)其他活跃性危险:①饥饿:线程无法访问它所需要的资源而不能继续执行,【避免使用线程优先级,因为这会增加平台依赖性,并导致活跃性问题,在大多数并发应用程序中。都可以使用默认的线程优先级。JVM需要把Thread API中定义的优先级映射到操作系统的调度优先级,很有可能是不同Java优先级被映射到同一个优先级中】;②糟糕的响应性:长任务和短任务;不良的锁管理,长时间地占用锁;③活锁:该问题尽管不会阻塞线程,但也不能继续执行,因为线程将不断重复执行相同的操作,而且总会失败。【表现】:错误地将不可修复的错误作为可修复的错误;多个相互协作的线程都对彼此进行响应从而修改各种的状态,并使得任何一个线程都无法继续执行,类似让路问题。【解决办法】:在重试机制中引入随机性:等待随机长度的时间和回退(网络协议)。

4Amdahl定律:在增加计算资源的情况下,程序在理论上能够实现最高加速比,这个值取决于程序中可并行组件与串行组件所占的比重。在所有并发称心中都包含了一些串行部分。

5)线程引入的开销:①上下文切换;②内存同步(可见性);③阻塞:竞争的同步,JVM在实现阻塞行为时,可以采用自旋等待(通过循环不断地尝试获取锁,直到成功)或者通过操作系统挂起被阻塞的线程。

6减少锁的竞争:①减少锁的持有时间(缩小锁的范围:快进快出);②降低锁的请求频率(减小锁的粒度:锁分解和锁分段,避免热点域);③使用带有协调机制的独占锁(如:并发容器、读-写锁、不可变对象、原子变量),这些机制允许更高的并发性。

7)并发测试:①安全性测试:采用测试不变条件的形式,即判断某个类的行为是否与其他规范保持一致。②活跃性测试;③性能测试:吞吐量(一组并发任务中已完成任务所占的比例)、响应性(请求从发出到完成之间的时间,延迟)、可伸缩性(增加更多资源的情况下,吞吐量的提示情况)。

 

 

你可能感兴趣的:(java,thread,并发)