发布时间:2025-12-10 11:29:23 浏览次数:4
首先需要明确的一点,为什么要使用多线程?
在一般人看来,使用多线程是为了加快程序运行的速度。
其实不然,使用多线程最主要的原因是提高系统的资源利用率。
现在的CPU基本都是多核的,如果只用单线程的话,就只用到了一个核心,其他的核心都在闲置。
CPU的核心有五个,如果只使用一个核心,那工作效率如何?比如现在有5个程序需要执行。
在单线程的时候,一个程序的执行需要10分钟,但是后面的程序都得等一个核心,那总共花费的时间就是50分钟。
而在多线程情况下,一个程序开始执行,后面的程序发现还有别的核心,就去别的核心执行,而不是傻瓜式地等同一个核心。
事实上,可以把CPU调度看做IO操作,IO操作相对于CPU而言是非常慢的,CPU等待IO那段时间是空闲的。如果需要做类似IO这种慢的操作,可以开多个线程来,尽量不要让CPU空闲下来,这样才能提高CPU的资源利用率。
说白了,其实多线程就是在压榨CPU的资源。将CPU原有的资源,充分的利用起来。
多线程不是万金油,并不是说线程越多,CPU的资源利用效率就越好。执行IO操作时线程可以适当的多一些,因为很多时候CPU是相对空闲的。如果是计算型的操作,本来CPU就不空闲,还开很多线程就更忙了。(有多线程就会有线程切换的问题,线程切换是需要耗费资源的)
多线程使用的地方其实很多,只是平时意识不到它的存在。
比如使用的最多的Web容器,Tomcat,它就是以多线程去响应请求的,我们可以在server.xml中配置连接池的配置。
<Connector port="8080" maxThreads="350" maxHttpHeaderSize="8192" minSpareThreads="45" maxPostSize="512000" protocol="HTTP/1.1" enableLookups="false" redirectPort="8443" acceptCount="200" keepAliveTimeout="15000" maxKeepAliveRequests="-1" maxConnections="25000" connectionTimeout="15000" disableUploadTimeout="false" useBodyEncodingForURI="true" URIEncoding="UTF-8" />Tomcat处理每一个请求都会从线程连接池里面用一个线程去处理,这显然是多线程操作。然后这个请求线程找到Servlet,执行对应的service方法。
而service方法是无状态的,多个线程请求service方法,往往没有操作共享变量,不操作共享变量就不会有线程安全问题。
平时常用的SpringMVC其实也是一样的,底层也是Servlet
还有在连接数据库的时候,也会用对应的连接池 (Druid,C3P0等),比如场景的Druid配置:
<bean init-method="init" destroy-method="close"> <property name="url" value="${jdbc_url}" /><property name="username" value="${jdbc_user}" /><property name="password" value="${jdbc_password}" /><property name="filters" value="stat" /><property name="maxActive" value="20" /><property name="initialSize" value="1" /><property name="maxWait" value="60000" /><property name="minIdle" value="1" /><property name="timeBetweenEvictionRunsMillis" value="60000" /><property name="minEvictableIdleTimeMillis" value="300000" /><property name="testWhileIdle" value="true" /><property name="testOnBorrow" value="false" /><property name="testOnReturn" value="false" /><property name="poolPreparedStatements" value="true" /><property name="maxOpenPreparedStatements" value="20" /><property name="asyncInit" value="true" /></bean>常见的问题
控制器触发了以后,我们直接将这个任务交给一个线程池去处理,交由线程池后就直接返回SUCCESS。
这样做的好处是什么?如果多个任务同时触发,那可能某些任务执行时间过长,请求可能会被阻塞住,而我们如果放在线程池中可以提高系统的吞吐量。
使用线程池的时候,往往我们的调用方都不需要考虑请求是否立马处理成功。假设线程池在处理任务的时候因为某些原因失败了,我们可以走报警机制(用邮件/短信等渠道去提醒请求方即可)。
在使用线程池的时候,很多时候我们也是把他当做异步来使用,只要我们的系统之间交互不是强一致性的,又希望提高系统的吞吐量,我们就可以考虑使用线程池。