多线程使用的主要目的在于:
1、吞吐量:你做WEB,容器帮你做了多线程,但是他只能帮你做请求层面的。简单的说,可能就是一个请求一个线程。或多个请求一个线程。如果是单线程,那同时只能处理一个用户的请求。
2、伸缩性:也就是说,你可以通过增加CPU核数来提升性能。如果是单线程,那程序执行到死也就利用了单核,肯定没办法通过增加CPU核数来提升性能。
鉴于你是做WEB的,第1点可能你几乎不涉及。那这里我就讲第二点吧。
--举个简单的例子:
假设有个请求,这个请求服务端的处理需要执行3个很缓慢的IO操作(比如数据库查询或文件查询),那么正常的顺序可能是(括号里面代表执行时间):
a、读取文件1 (10ms)
b、处理1的数据(1ms)
c、读取文件2 (10ms)
d、处理2的数据(1ms)
e、读取文件3 (10ms)
f、处理3的数据(1ms)
g、整合1、2、3的数据结果 (1ms)
单线程总共就需要34ms。
那如果你在这个请求内,把ab、cd、ef分别分给3个线程去做,就只需要12ms了。
所以多线程不是没怎么用,而是,你平常要善于发现一些可优化的点。然后评估方案是否应该使用。
假设还是上面那个相同的问题:但是每个步骤的执行时间不一样了。
a、读取文件1 (1ms)
b、处理1的数据(1ms)
c、读取文件2 (1ms)
d、处理2的数据(1ms)
e、读取文件3 (28ms)
f、处理3的数据(1ms)
g、整合1、2、3的数据结果 (1ms)
单线程总共就需要34ms。
如果还是按上面的划分方案(上面方案和木桶原理一样,耗时取决于最慢的那个线程的执行速度),在这个例子中是第三个线程,执行29ms。那么最后这个请求耗时是30ms。比起不用单线程,就节省了4ms。但是有可能线程调度切换也要花费个1、2ms。因此,这个方案显得优势就不明显了,还带来程序复杂度提升。不太值得。
那么现在优化的点,就不是第一个例子那样的任务分割多线程完成。而是优化文件3的读取速度。
可能是采用缓存和减少一些重复读取。
首先,假设有一种情况,所有用户都请求这个请求,那其实相当于所有用户都需要读取文件3。那你想想,100个人进行了这个请求,相当于你花在读取这个文件上的时间就是28×100=2800ms了。那么,如果你把文件缓存起来,那只要第一个用户的请求读取了,第二个用户不需要读取了,从内存取是很快速的,可能1ms都不到。
伪代码:
public class MyServlet extends Servlet{ private static Map<String, String> fileName2Data = new HashMap<String, String>(); private void processFile3(String fName){ String data = fileName2Data.get(fName); if(data==null){ data = readFromFile(fName); //耗时28ms fileName2Data.put(fName, data); } //process with data } }
看起来好像还不错,建立一个文件名和文件数据的映射。如果读取一个map中已经存在的数据,那么就不不用读取文件了。
可是问题在于,Servlet是并发,上面会导致一个很严重的问题,死循环。因为,HashMap在并发修改的时候,可能是导致循环链表的构成!!!(具体你可以自行阅读HashMap源码)如果你没接触过多线程,可能到时候发现服务器没请求也巨卡,也不知道什么情况!
好的,那就用ConcurrentHashMap,正如他的名字一样,他是一个线程安全的HashMap,这样能轻松解决问题。
public class MyServlet extends Servlet{ private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>(); private void processFile3(String fName){ String data = fileName2Data.get(fName); if(data==null){ data = readFromFile(fName); //耗时28ms fileName2Data.put(fName, data); } //process with data } }
这样真的解决问题了吗,这样虽然只要有用户访问过文件a,那另一个用户想访问文件a,也会从fileName2Data中拿数据,然后也不会引起死循环。
可是,如果你觉得这样就已经完了,那你把多线程也想的太简单了,骚年!
你会发现,1000个用户首次访问同一个文件的时候,居然读取了1000次文件(这是最极端的,可能只有几百)。What the fuckin hell!!!
难道代码错了吗,难道我就这样过我的一生!
好好分析下。Servlet是多线程的,那么
public class MyServlet extends Servlet{ private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>(); private void processFile3(String fName){ String data = fileName2Data.get(fName); //“偶然”-- 1000个线程同时到这里,同时发现data为null if(data==null){ data = readFromFile(fName); //耗时28ms fileName2Data.put(fName, data); } //process with data } }
上面注释的“偶然”,这是完全有可能的,因此,这样做还是有问题。
因此,可以自己简单的封装一个任务来处理。
public class MyServlet extends Servlet{ private static ConcurrentHashMap<String, FutureTask> fileName2Data = new ConcurrentHashMap<String, FutureTask>(); private static ExecutorService exec = Executors.newCacheThreadPool(); private void processFile3(String fName){ FutureTask data = fileName2Data.get(fName); //“偶然”-- 1000个线程同时到这里,同时发现data为null if(data==null){ data = newFutureTask(fName); FutureTask old = fileName2Data.putIfAbsent(fName, data); if(old!=null){//原文:old==null data = old; }else{ exec.execute(data); } } String d = data.get(); //process with data } private FutureTask newFutureTask(final String file){ return new FutureTask(new Callable<String>(){ public String call(){ return readFromFile(file); } private String readFromFile(String file){return "";} } } }
以上所有代码都是直接在bbs打出来的,不保证可以直接运行。
多线程最多的场景:web服务器本身;各种专用服务器(如游戏服务器);
多线程的常见应用场景:
1、后台任务,例如:定时向大量(100w以上)的用户发送邮件;
2、异步处理,例如:发微博、记录日志等;
3、分布式计算
相关推荐
详细介绍了java中多线程的应用!对于初学java的同学们 很有帮助!
### Java多线程文章系列知识点概述 #### 一、Java多线程编程...以上是《Java多线程文章系列》的主要知识点概述,涵盖了从多线程的基础概念到高级应用,希望能帮助读者深入理解Java多线程编程的核心技术和实践技巧。
在Java编程语言中,多...同时,理解并掌握这些同步机制的原理和使用场景,对于提升Java多线程编程的能力至关重要。在实际开发中,要特别注意死锁、活锁和饥饿等问题,避免因线程同步不当而导致的性能下降或程序错误。
Java多线程编程是Java开发中的一项重要技术,它允许程序同时执行多个任务,提高了系统效率和响应速度。然而,多线程环境下的编程也带来了一些挑战,如数据同步、线程安全等问题。本文将重点讨论Java多线程编程中需要...
在安卓应用开发中,多线程是至关重要的技术,它能帮助我们实现应用程序的高效运行,尤其是在处理耗时操作如网络请求、大数据处理等场景。本文将深入探讨如何在安卓中进行多线程编程,主要关注`Thread`、`Handler`的...
### Java多线程—线程间的通信 #### 一、线程间的通信 ##### (1)为什么要处理线程间的通信? 在多线程环境中,不同的线程可能需要协同工作来完成一项任务。例如,一个线程负责生产数据,另一个线程负责消费这些...
本资源概括了 Java 的基础知识,涵盖 String、接口和抽象类、泛型、IO 流、反射和多线程等方面。 String String 是 Java 中最基本的数据类型之一,用于存储文本数据。String 是不可变的,意味着一旦创建了 String ...
"JAVA实验典型举例"这个资源提供了一系列的Java实例,旨在帮助学习者深入理解和应用Java的核心概念。通过分析这些代码,我们可以强化理论知识,提高解决问题的能力。 首先,Java是一种面向对象的编程语言,它的主要...
通过对Market类以及其关联的Producer和Customer类的分析,我们可以了解到如何在实际场景中应用这些线程概念,例如模拟商品市场的供需关系,Producer线程负责生产商品,Customer线程负责消费,而Market作为管理者,...
Java多线程编程中,读写锁是一种优化并发访问共享资源的有效工具,它允许多个线程同时读取,但只允许一个线程写入。Java的`java.util.concurrent.locks.ReentrantReadWriteLock`类提供了可重入的读写锁功能。下面...
3. 高级阶段:涵盖 Java 的高级知识,如多线程编程、数据库编程、Web 开发等。 关于课程的说明 本课程旨在帮助零基础的学习者快速掌握 Java 语言,课程内容涵盖了 Java 的基础知识、开发场景和应用领域,课程的...
### Java多线程概念 #### 进程与线程的区别 进程是资源分配的基本单位,而线程是操作系统能够进行运算调度的最小单位。一个进程中可以包含多个线程,这些线程共享进程的资源。线程是进程中的一个实体,是CPU调度和...
1.3 JAVA开发场景举例 - 1.3.1 SSM(Spring+SpringMVC+MyBatis):这是Java Web开发中常见的技术栈,用于构建高效、灵活的企业级应用。 - 1.3.2 ANDROID核心代码:Java是Android应用的主要开发语言,深入理解Java有...
三、多线程应用场景 主要能体现到多线程提高程序效率。举例:分批发送短信、迅雷多线程下载等。 四、多线程安全 当多个线程同时共享,同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是...
**线程局部变量(ThreadLocal)是Java编程中一个非常重要的工具类,它在多线程环境下提供了线程安全的数据存储。ThreadLocal并不是一个变量,而是一个类,它为每个线程都创建了一个独立的变量副本,使得每个线程都...
- **选择依据**:根据具体应用场景选择合适的模型,如需要高效并行处理且共享资源的场景适合使用多线程;需要独立资源管理和高健壮性的场景则更适合多进程。 #### 三、Java中实现多线程的方式 1. **继承Thread类**...
1. Java 的类加载器体系结构和双亲委托机制,应用场景举例。 2. JVM 内存结构,GC 算法,参数调优;JVM 的内存结构,String 常量池的考核。 3. 内存分配策略;垃圾回收器种类,垃圾回收扫描算法(root 扫描),回收...
《Java并发编程》一书是由著名并发编程专家Doug Lea所著,他同时也是Java并发包(JUC)的作者,这本书详细介绍了Java多线程编程的基础概念和高级技术。 首先,书中提到了并发编程的基本概念,包括并发模型、设计力...
7. 加入多线程 8. 使用集合(可选) 二.设计场景,实现其要求 场景设计在一间公司,拥有很多的小猫小狗等宠物可以出卖。 1. 输出9只动物的全部信息,举例代表全部动物的信息,并用List集合存放所有信息。 2. 可以...
- 题目:说明Java反射机制的作用和应用场景。 - 答案:反射允许在运行时动态获取类信息,创建和操作对象,实现动态代理,便于实现插件化或框架设计。 7. **设计模式** - 题目:举例说明单例模式的实现方式及其优...