Java并发编程(线程池)

版权声明:转载标明来源! https://blog.csdn.net/qq_39213969/article/details/88902191

一,线程池(Executor 是任务执行器,execute是执行任务(Runnable接口的)内部就会创建一个线程来执行这个任务,,过程中通常使用ExecutorService种的submit()方法来拿取结果异常)

线程池中 submit()和 execute()方法有什么区别?
接收的参数不一样:1.submit有返回值,而execute没有 2.submit方便Exception处理

1.名称的意思是,线程池管理的是一个工作者线程的同构池。其中线程池与工作队列密切相关,
(工作队列:持有所有等待执行的任务)

2.工作者线程的工作变得轻松:每次从工作队列中获取下一个任务,执行它,然后回来继续等待另一个线程。

形象的描述:

线程池形象的表示就是存放应用程序中使用的线程的一个集合(就是放线程的地方,这样线程都放在一个地方就好管理了)。CLR初始化时,线程池中是没有线程的,在内部, 线程池维护了一个操作请求队列,当应用程序想执行一个异步操作时,就调用一个方法,就将一个任务放到线程池的队列中,线程池中代码从队列中提取任务,将这个任务委派给一个线程池线程去执行,当线程池线程完成任务时,线程不会被销毁,而是返回到线程池中,等待响应另一个请求。由于线程不被销毁, 这样就可以避免因为创建线程所产生的性能损失。

3、通过线程池的工作者线程实现异步

3.1 创建工作者线程的方法

public static bool QueueUserWorkItem (WaitCallback callBack);

public static bool QueueUserWorkItem(WaitCallback callback, Object state);

这两个方法向线程池的队列添加一个工作项(work item)以及一个可选的状态数据。然后,这两个方法就会立即返回。

工作项其实就是由callback参数标识的一个方法,该方法将由线程池线程执行。同时写的回调方法必须匹配System.Threading.WaitCallback委托类型,定义为:public delegate void WaitCallback(Object state);

4.创建线程池方法。

(1)newfixedthreadpool: 定长的,后面不变,之后如果一个线程抛出异常终止销毁,在创建一个线程补齐。
(2)newcachedthreadpool: 缓存的,非定长,之后如果太多空闲就收回空间内存,如果不够再灵活的创建补齐。
(3)newsinglethreadexecutor: 单线程化的executor 只创建一个单一的方法进行执行任务,如果这个异常结束,那么就在创建建一个线程去代替原有的线程。按照队列方式工作,同步的工作不利于高并发。
(4)newscheduledthreadpool: 创建一个定长的线程池,而且支持定时以及周期性的任务执行。类似于Timer。

5.newfixed和newcached这两个工厂方法穿件threadpoolexecutor的实例。
web服务器采用Executor创建了一个有界的线程池,通过executor方法把任务提交给工作队列,然后工作线程直接从工作队列中取出工作任务进行执行,周而复始。

6,这样子服务器的稳定得到了保证,不会有很多的线程去争夺资源cpu,所以服务器会平缓的恶化。
注意:EXecutor框架的好处:可以调优,管理,监视,记录日记,错误报告,,,,,。如果没有这个任务执行框架那么添加这些特性是很困难的。

二,减少锁的竞争

1.减少线程持有锁得时间
2.减少线程请求锁的频率以及次数
3.用协调的方式取代独占锁,。从而允许更高的并发性。

具体方法

4.减少锁的范围(“达到快进快出的效果”)例如:把与锁无关的代码移除出·synchronized快来实现,其实也就是缩短持有锁的时间。
5.减小锁的粒度(“实际上就是减少线程调用锁的频率”)具体实现时|:通过分拆锁以及分离锁,就是减少锁的粒度。实际上就是采用相互独立的锁,守卫多个独立的状态变量,在改变之前他们都是有同一个大的锁进行守护的。以上这些技术减少了锁发生的2粒度,潜在实现了更好的可伸缩性。

**注意:**如果锁的守卫数量大于一,而且是相互独立的状态变量,就可以通过分拆锁·以及分离锁进行,几个不同的小锁守护不同的状态变量,结果是每个锁被访问的频率都降低了。减少了锁的竞争。

6.分拆锁:(把一个竞争激烈的锁拆分成两个锁,很可能形成两个竞争激烈的锁。尽管这可以通过两个并发执行,取代一个线程,从而对可伸缩性进行一定的改造。)但是并不能提高多个处理器在同一个系统中的高并发执行的前景。

7.分离锁:(分拆锁可以被扩展,分成可大可小的加锁块的集合,并且它们归属于相互独立对象,这样子的情况就是分离锁) ,最典型的分离锁的集合就是concurrenthashmap 这个并发集合,线程是安全的

优点

注意:concurrenthashmap 使用了 16个锁的Array,每一个锁都守护Hash Bucket 的1/16,,,,第n个数字,N由第N/16 个锁进行守护,,,这项技术使得 ConcurrentHashMap 能够支持16个线程的并发访问。

缺点(负面作用)

1.对容易加锁对于独占访问就会更加的困难,并且更加的昂贵了。
2.对整个容器加锁(独占锁的情况):它的值需要被扩展,重排,放入一个更大的Bucket中时,这将要是一个线程或者所有的分离锁,就是要获取独占锁。

8.避免热点域

(1)当每一个操作都请求变量时,锁的粒度很难进行降低了。因此这种情况下常用的优化方法便是:比较缓存常用的计算结果,会引入“热点域”,从而限制可伸缩性。
(2)限制可伸缩性的方法:引入计数器,即使你对每一个哈希链度使用锁的分离,但是对计数器独占锁的同步访问还是重新引入了可伸缩问题。这看起来是一个性能的优化-缓存size操作的结果-却已经转换为与为可可伸缩性问题。这种情况下计数器被称为热点域,因为每个操作都要访问到他。。

为了避免这个问题: ConcurrentHashMap 通过枚举每个条目获取size,并把这个值加入每个条目中,而不是维护一个全局的计数器。。为了避免列举所有的元素, ConcurrentHashMap 为每一个条目维护一个独立的计数域,同样由分离的锁守护。。

9.独占锁替代方法:(提前使用独占锁:并发容器,读写锁,不可变对象,原子变量)

10.读写锁枷锁规则:(多读者单写者:多个读者并发访问资源,但是写者必须独占锁,提高并发性)

11.原子变量:提供了减少更新热点域的方式(同时减少热点域的开销),例如:静态计数器,序列发生器,对链表数据结构头结点引用)。

12.减少上下文切换的开销:(拆分工作,把I/O操作放到一个线程,是用户看不到他的开销,把所有日志开销I/O移入另一个线程。消除输出流竞争,减少了竞争源。上下文切换减少,锁的管理更加简单,需要调度的资源也更加的少了);

猜你喜欢

转载自blog.csdn.net/qq_39213969/article/details/88902191