`
coral0212
  • 浏览: 101399 次
  • 性别: Icon_minigender_1
  • 来自: 合肥
社区版块
存档分类
最新评论

java高效之线程池之并发之安全队列之我见(实际应用场景篇)

 
阅读更多

   

    由于很少在社区里发表自己手敲的文章,因为水平太低(语文是数学老师教的),自惭形秽,不怕被拍砖,但怕遭鄙视,畏首畏尾的,即使胡乱说一通,也是无关技术重点的。

 

    常常在面试和被面试的时候,作为偶尔代替考官和经常被面的我,时时刻刻都会谈及到线程安全,并发,安全队列,线程池,反射技术,设计模式,场景应用举例,优先级,同步锁,架构设计要素,技术选型,比较相关技术优劣,辞职原因,期望薪酬,职业规划和目标等等。

 

    当然本文的重点还是说说线程池,并发,安全队列等应用场景,后面再各个说明。其中,谈到的相对比较多的要数多线程开发经验和线程安全等问题了。也是吾辈最为棘手的边缘技术(高手勿喷),之所以说边缘是因为在常规的一些web开发中,程序员亲身经历的相关经验的确 少之又少,一般都是技术大拿前期做好了相应框架和测试,达到了测试条件,程序员就是codeing再codeing,至于框架的优劣无法评估,其中的精妙更无从谈起。也许这段话会遭来无数的吐槽和拍砖,但是但是也烦请体谅体谅一下底层的coder一个复杂的心情。

 

    言归正传吧,首先来说说线程池和工作队列的概念。详细可以看看JERRY_XING8,文章写的非常好,言简意赅的摘录一下:

 

    场景:Web 服务器、数据库服务器、文件服务器或邮件服务器之类的许多服务器,接下来会多举例一些实际应用中的场景来详细说明一下。

    

    注:两个虚线框分别表示线程A和线程B恩能够访问的数据边界,由此可见 任务队列是线程间通信的媒介。

 

    生产者消费者模型在软件设计中是极其常见的模型,常常被用来实现对各个组件或系统解耦合。大到分布式的系统交互,小到网络层对象和应用层对象的通讯,都会应用到生产者消费者模型,在任务队列中,生产和消费的对象为“任务”。这里把任务定义为组合了数据和操作的对象。

 

        首先我们需要清楚在设计队列表时会遇到哪些挑战: 

       1)表的读写。

              由于入队列和出队列是相互影响的,在高负载下可能会导致锁竞争、事务死锁、IO超时等等。

       2)当多个接收者试图从同一队列读数据时,它们会随机地获取重复项,因而导致重复的处理过程。

              你需要在队列上实现一些高性能的行锁定,以便让并发接受器不会接收相同的数据项。

       3)队列表需要以某种顺序存储行以及以某种顺序读取行,这使得设计索引很棘手。

              队列表并不总是遵守先进先出的,有时候顺序中的消息带有更高的优先级,无论这个消息是否入队列都要先处理它。

       4)队列表需要以XML或二进制的形式序列化对象,这使得存储和重建索引很麻烦。

              你不能在队列表中重建索引,因为它包含了文本或二进制字段。因此,每过一天,数据表会变得越来越慢,最后查询会超时,你不得不关闭服务并重建索引。

       5)出队列的过程中,一批行数据被选中、被更新,然后返回数据。

              你需要一个"State"(状态)列来定义数据项的状态。出队列时,你只需选择某个状态的数据项。现在状态只有几种类型:PENDING(待定)、PROCESSING(处理中)、PROCESSED(已处理)、ARCHIVED(存档)。你不能在状态列上创建索引,因为不能提供足够的选择性,具有相同的状态的数据行有成千上万。因此,任何出队列操作都会导致集群的索引被重新扫描,这属于CPU和IO密集型操作,会产生锁竞争。

       6)在出队列的过程中,你不能仅仅移除队列表的相关行,因为这很容易导致数据表产生存储碎片。而且,你还需要重新处理订单/任务/通知做N次操作以防止这些操作在第一次中失败。这意味着存储行数据需要更长的时间、索引会持续增长以及出队列越来越慢。

       7)你必须归从入队表把处理过的数据项归档到不同的数据表或数据库,以保持主队列表的精简。这意味着需要移动大量的带有特定状态的数据行到另一个数据库。如此大的数据移动会频繁产生存储碎片,以至于降低入队列和出队列的性能。

       8)你有24×7不间断的业务。你不能停止服务再归档大量的行数据。这意味者你必须在不影响入栈和出栈通信的情况下持续的归档行数据。

   

       下面来说说场景,mvc框架中最常用的,都知道的数据库操作类Dao文件,如表A就叫aDao这样的bean存在。比如说数据库的锁机制,级别较高,查询锁,如果在一个原子事务操作中,用到表A,这时事务完成之前,A表是被锁住的。此时又来了一个线程,也用到A表操作,也用到aDao这个实例化对象,此事,查询都超时了,超时出错了。该怎么办?这里的aDao就是图中的线程B操作,是执行任务的,线程A就是本实例的业务请求。如果,假如线程A这样的任务成千上万个,aDao在spring的声明是单例模式,那么在上下文环境中,aDao始终只有一个实例化的对象,如果说,用到队列,在此实例中如何应用?如果不用,该怎么来解决这个问题?

 

    实例分析:

    1、在最常见的本例中,涉及到数据库表操作,表的操作事务级别较高,只要一发生,立刻加锁直到事务结束。此时如果采用线程池,形成多个(或最优化的个数)aDao实例,这么多执行任务线程,在对A表操作的时候,同样需要等待排队,唤醒,终止,有瓶颈存在。因此线程池byebye!

 

        2、牺牲事务的原子性,就事务定义在Dao层,出错不能自动回滚,代码中估计自己定义手动回滚。此时,Dao的操作单个任务处理的时间很短,任务处理比较快,可复用。但是业务上尽量避免脏读赃写等操作。这时可用线程池,这里的线程池如何应用?

 

        3、将队列放到业务操作上,也即将事务原子操作作为队列,等待一个业务操作完毕,再进行下一个业务操作,可以,但是不符合耦合性。pass!

        

        针对第二点,引出了一个新的命题,线程池何时用,该怎么用?(以下摘自kavensu

        

         1.单个任务处理的时间比较短 

         2.将需处理的任务的数量大(大到什么程度,这里估算在每秒几百到几千个任务) 
这样其好处也是显而易见的,
       1.减少在创建和销毁线程上所花的时间以及系统资源的开销 

        2.如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存以及”过度切换”。

 

 

//初使化线程池,这是JDK5.0中自带的线程池,这里的参数依次代表:
//核心线程数(最小活动线程数)
//最大线程数及并发数【这个要注意,如果你的实际发大于该数,则有些请求这个时候虽然被接收,但是去得不到处理,这个数据一定得根据实际情况而设定,如我这里设值为20,实际模拟并发50,如循环一次,或者是二次并发,总会有20个不能够处理,如果设为25,就有15得不到处理,如果设为50则全部可以被处理,这个可以折磨了我好几天】
//线程池维护线程所允许的空闲时间
//线程池维护线程所允许的空闲时间的单位
//线程池所使用的缓冲队列
//线程池对拒绝任务的处理策略(通常是抛出异常)
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 100, 10,TimeUnit.SECONDS, 
	new ArrayBlockingQueue<Runnable>(10),
	new ThreadPoolExecutor.DiscardOldestPolicy());
while(true){
	//侦听
	server.clientSocket=server.serverSocket.accept();
	if(server.clientSocket!=null)
	{	
		//采用线程池启动线程
		threadPool.execute(new MsgListener(server.clientSocket));
	}
}

 

 

另有网友总结:(觉得很合理,转)

        1.Executor=线程池

        2.使用多线程时,尽量做到与线程上下文传递无关,通常放到调用链的最底层。比如hibernate的session是绑定到线程变量上的,在多线程的情况下,新线程会由于不是同一session而无法lazyload.

        3.异步的一个主要实现就是新起线程执行,主线程不用等待。  ScheduledThreadPoolExecutor较Timer的一个优势就是线程池异步执行。Timer由于是单线程执行队列。肯定没有前者快。(提问:如果说现在有个请求是同步的,就是有返回值的,使用多线程并发处理合理吗?还是说多线程基本上就是异步的代名词?)

 

 

最后欢迎大家拍砖,说说大家对线程池的实际应用场景及实现思路等等。是不是坚持写博客,才有回复啊,好恐怖!

  • 大小: 5.7 KB
  • 大小: 8.2 KB
  • 大小: 13.5 KB
  • 大小: 13.5 KB
  • 大小: 10.8 KB
  • 大小: 8.4 KB
分享到:
评论

相关推荐

    Java实现的线程池、消息队列功能

    在实际应用中,根据业务需求,我们可以自定义线程池配置,如设置合适的线程数量和队列大小,以优化系统的资源利用率和性能。同时,消息队列的使用可以改善系统的吞吐量和可靠性,提高系统的整体效率。 关于“工具”...

    并发-线程池和阻塞队列

    线程池和阻塞队列的组合在实际应用中非常广泛,例如在高并发的Web服务器中,线程池用于处理来自客户端的请求,阻塞队列作为请求的缓冲区,可以有效地平滑突发的流量,防止因短时间内大量请求导致的服务器过载。...

    并发-线程池和阻塞队列.pdf

    线程池和阻塞队列是实现并发的关键组件之一。本文将详细介绍线程池原理、使用场景及注意事项,以及阻塞队列的相关知识。 首先,线程池是一种基于池化思想管理线程的技术,它可以重用一组线程执行多个任务。线程池的...

    线程池&&队列各类区别使用场景

    - **并发队列(ConcurrentQueue)**:线程安全的队列,允许多个线程同时进行读写操作。 - **优先级队列(PriorityQueue)**:按照元素的优先级排序,优先级高的元素先出队。 - **双端队列(Deque)**:支持两端插入...

    java线程池封装j

    Java线程池是一种高效管理线程的技术,它允许开发者预定义一组线程,根据任务的需要灵活调度,而不是每次需要执行任务时都创建新的线程。这种设计模式大大提高了系统的性能,减少了系统资源的消耗,特别是在高并发...

    java 四种线程池实例

    Java线程池是一种高效管理线程的技术,它可以帮助开发者更好地控制并发执行的线程数量,避免资源浪费,提高系统性能。在Java中,线程池是通过`java.util.concurrent`包中的`ExecutorService`接口及其实现类来实现的...

    java.util.concurrent 实现线程池队列

    总的来说,`java.util.concurrent` 提供的线程池和工作队列机制,使得Java开发者能够高效、安全地管理并发任务,优化系统资源的利用,提高系统的整体性能。理解并熟练运用这些工具,是每个Java开发者必备的技能。

    Java分布式应用学习笔记07线程池应用

    #### Java线程池实现 在Java中,`java.util.concurrent.ExecutorService`接口提供了创建和管理线程池的能力,而`ThreadPoolExecutor`类则是其最常用的实现之一。`ThreadPoolExecutor`允许我们自定义线程池的关键...

    Java几种线程池类型介绍及使用.docx

    Java线程池是一种高效管理线程的机制,它克服了直接使用`new Thread()`创建线程的诸多弊端。创建线程池的主要目的是重用已存在的线程,减少新对象的创建和销毁开销,从而提升程序性能。此外,线程池还可以通过控制...

    JAVA经典线程池源码

    Java线程池是Java并发编程中的重要组成部分,它在多线程编程中扮演着至关重要的角色,有效地管理和调度线程资源,提高了程序的性能和稳定性。本资源包含了一个经典的Java线程池实现,适用于大型项目,能帮助开发者...

    Android中的线程池与任务队列

    在Android开发中,高效地管理线程和任务执行是至关重要的,这关乎到应用的性能、响应速度以及用户体验。线程池和任务队列是实现这一目标的关键工具。本文将深入探讨Android中线程池与任务队列的概念、工作原理以及...

    java线程池实例详细讲解

    Java线程池是一种高效管理线程资源的工具,它能够帮助开发者有效地控制并调度线程,从而提升系统性能,减少系统资源的浪费。在Java中,`ExecutorService`接口是线程池的主要入口,它是`java.util.concurrent`包的一...

    Java编程中线程池的最大风险规避

    ### Java编程中线程池的最大风险规避 #### 死锁 在Java编程中,线程池带来的一个显著风险就是死锁。死锁是指多个线程因互相等待对方持有的资源而不释放自身资源,导致无限期等待的情况。对于一般的多线程程序而言...

    springmvc+spring线程池处理http并发请求数据同步控制问题

    1. Spring提供了一个名为ThreadPoolTaskExecutor的实现,它基于Java的ExecutorService接口,允许我们自定义线程池配置,如核心线程数、最大线程数、队列容量、超时时间等。 2. 通过在配置文件中声明一个...

    Java线程池使用说明

    Java线程池是Java并发编程中的重要组件,它能够有效地管理和复用线程,从而提高程序的执行效率和降低资源消耗。在JDK 1.5版本之前,Java对线程池的支持非常有限,而在JDK 1.5之后,加入了java.util.concurrent包,...

    并发容器和线程池,java并发编程3

    - **无界队列**:理论上可以无限存储元素,但在实际应用中会受到内存限制。 - **高性能**:适用于大量线程进行读取操作的场景。 - **非阻塞性**:使用CAS(Compare and Swap)而非传统的锁机制来保证线程安全。 **...

    BlockingQueue队列自定义超时时间取消线程池任务

    在Java编程中,`BlockingQueue`是一个非常重要的并发工具类,它主要用于线程间的数据通信。`newFixedThreadPool`是`java.util.concurrent`包中的一个线程池工厂方法,用于创建固定数量线程的线程池。`FutureTask`则...

    Java 线程池.docx

    Java线程池是一种高效管理线程资源的工具,它的出现是为了应对多线程编程中频繁创建和销毁线程带来的性能开销以及资源消耗。在Java中,通过使用线程池,我们可以预先创建一定数量的线程,这些线程在空闲时可以被复用...

    java多线程加队列上传文件_后台处理

    4. **错误处理**:在实际应用中,需要更加完善地处理各种异常情况,例如在网络不稳定的情况下重试上传操作,以及在客户端断开连接时及时释放资源等。 5. **安全性**:对于敏感信息(如用户名密码等)的传输,应该...

Global site tag (gtag.js) - Google Analytics